diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml index 1b2185be92cdd3..72957be0ba3c1a 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml @@ -17,7 +17,9 @@ description: | properties: compatible: - const: ite,it66121 + enum: + - ite,it66121 + - ite,it6610 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/display/panel/auo,a030jtn01.yaml b/Documentation/devicetree/bindings/display/panel/auo,a030jtn01.yaml new file mode 100644 index 00000000000000..fb2aef0061e7a0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/auo,a030jtn01.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/auo,a030jtn01.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AUO A030JTN01 3.0" (320x480 pixels) 24-bit TFT LCD panel + +description: | + The panel must obey the rules for a SPI slave device as specified in + spi/spi-controller.yaml + +maintainers: + - Paul Cercueil + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: auo,a030jtn01 + + backlight: true + port: true + power-supply: true + reg: true + reset-gpios: true + +required: + - compatible + - reg + - power-supply + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include + + panel@0 { + compatible = "auo,a030jtn01"; + reg = <0>; + + spi-max-frequency = <10000000>; + + reset-gpios = <&gpe 4 GPIO_ACTIVE_LOW>; + power-supply = <&lcd_power>; + + backlight = <&backlight>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/hwlock/ingenic,vpu-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/ingenic,vpu-hwspinlock.yaml new file mode 100644 index 00000000000000..b08dfd3feea9a8 --- /dev/null +++ b/Documentation/devicetree/bindings/hwlock/ingenic,vpu-hwspinlock.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwlock/ingenic,vpu-hwspinlock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ingenic VPU Hardware Spinlock + +maintainers: + - Paul Cercueil + +description: + The Hardware Spinlock of the VPU provides a mutex mechanism between the main + processor and the co-processor present in the VPU IP. + +properties: + compatible: + oneOf: + - enum: + - ingenic,jz4755-vpu-hwspinlock + - items: + - enum: + - ingenic,jz4760-vpu-hwspinlock + - ingenic,jz4770-vpu-hwspinlock + - ingenic,jz4780-vpu-hwspinlock + - const: ingenic,jz4755-vpu-hwspinlock + + reg: + maxItems: 1 + + '#hwlock-cells': + const: 0 + +required: + - compatible + - reg + - '#hwlock-cells' + +additionalProperties: false + +examples: + - | + hwspinlock: hwlock@4 { + compatible = "ingenic,jz4755-vpu-hwspinlock"; + reg = <0x4 0xc>; + #hwlock-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/ingenic,i2c.yaml b/Documentation/devicetree/bindings/i2c/ingenic,i2c.yaml index af6d64a6da6e6c..89c417f9028fbb 100644 --- a/Documentation/devicetree/bindings/i2c/ingenic,i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/ingenic,i2c.yaml @@ -38,14 +38,15 @@ properties: enum: [ 100000, 400000 ] dmas: - items: - - description: DMA controller phandle and request line for RX - - description: DMA controller phandle and request line for TX + minItems: 1 + maxItems: 2 dma-names: - items: - - const: rx - - const: tx + oneOf: + - items: + - const: rx + - const: tx + - const: tx-rx required: - compatible diff --git a/Documentation/devicetree/bindings/mips/ingenic/devices.yaml b/Documentation/devicetree/bindings/mips/ingenic/devices.yaml index ee00d414df1001..52af1a3d3bf0ee 100644 --- a/Documentation/devicetree/bindings/mips/ingenic/devices.yaml +++ b/Documentation/devicetree/bindings/mips/ingenic/devices.yaml @@ -23,16 +23,68 @@ properties: - const: qi,lb60 - const: ingenic,jz4740 + - description: Wolsen LDK (horizontal) + items: + - const: wolsen,ldkh + - const: ingenic,jz4760 + + - description: Wolsen LDK (vertical) + items: + - const: wolsen,ldkv + - const: ingenic,jz4760b + + - description: Wolsen PlayGo + items: + - const: wolsen,playgo + - const: ingenic,jz4770 + - description: YLM RetroMini RS-90 items: - const: ylm,rs90 - const: ingenic,jz4725b + - description: YLM RS-97 + items: + - const: ylm,rs97 + - const: ingenic,jz4760 + + - description: YLM RG-300 + items: + - const: ylm,rg300 + - const: ingenic,jz4760b + + - description: YLM RG-99 + items: + - const: ylm,rg99 + - const: ingenic,jz4725b + - description: Game Consoles Worldwide GCW Zero items: - const: gcw,zero - const: ingenic,jz4770 + - description: YLM RG-350 + items: + - const: ylm,rg350 + - const: ingenic,jz4770 + + - description: YLM RG-350M + items: + - const: ylm,rg350m + - const: ingenic,jz4770 + + - description: YLM RG-280M + items: + - enum: + - ylm,rg280m-v1.0 + - ylm,rg280m-v1.1 + - const: ingenic,jz4770 + + - description: YLM RG-280V + items: + - const: ylm,rg280v + - const: ingenic,jz4770 + - description: MIPS Creator CI20 items: - const: img,ci20 diff --git a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml index fec1cc9b9a0851..d092c1e7f3ef61 100644 --- a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml +++ b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml @@ -40,6 +40,7 @@ properties: - brcm,bcm4359-fmac - cypress,cyw4373-fmac - cypress,cyw43012-fmac + - ingenic,iw8103-fmac - const: brcm,bcm4329-fmac - enum: - brcm,bcm4329-fmac diff --git a/Documentation/devicetree/bindings/phy/ingenic,phy-usb.yaml b/Documentation/devicetree/bindings/phy/ingenic,phy-usb.yaml index 5cab2164863211..10aad2651c3dc5 100644 --- a/Documentation/devicetree/bindings/phy/ingenic,phy-usb.yaml +++ b/Documentation/devicetree/bindings/phy/ingenic,phy-usb.yaml @@ -15,13 +15,19 @@ properties: pattern: '^usb-phy@.*' compatible: - enum: - - ingenic,jz4770-phy - - ingenic,jz4775-phy - - ingenic,jz4780-phy - - ingenic,x1000-phy - - ingenic,x1830-phy - - ingenic,x2000-phy + oneOf: + - enum: + - ingenic,jz4760-phy + - ingenic,jz4775-phy + - ingenic,jz4780-phy + - ingenic,x1000-phy + - ingenic,x1830-phy + - ingenic,x2000-phy + - items: + - enum: + - ingenic,jz4760b-phy + - ingenic,jz4770-phy + - const: ingenic,jz4760-phy reg: maxItems: 1 @@ -48,7 +54,7 @@ examples: - | #include otg_phy: usb-phy@3c { - compatible = "ingenic,jz4770-phy"; + compatible = "ingenic,jz4770-phy", "ingenic,jz4760-phy"; reg = <0x3c 0x10>; vcc-supply = <&vcc>; diff --git a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml index c2c370448b8177..21b8f2555ad11f 100644 --- a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml @@ -37,6 +37,7 @@ properties: - ingenic,jz4750-pinctrl - ingenic,jz4755-pinctrl - ingenic,jz4760-pinctrl + - ingenic,jz4760b-pinctrl - ingenic,jz4770-pinctrl - ingenic,jz4775-pinctrl - ingenic,jz4780-pinctrl diff --git a/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml b/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml index 46527038bf2274..5abf27a6da58e6 100644 --- a/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml @@ -10,6 +10,9 @@ title: Ingenic JZ47xx battery bindings maintainers: - Artur Rojek +allOf: + - $ref: power-supply.yaml + properties: compatible: oneOf: @@ -17,6 +20,8 @@ properties: - items: - enum: - ingenic,jz4725b-battery + - ingenic,jz4760-battery + - ingenic,jz4760b-battery - ingenic,jz4770-battery - const: ingenic,jz4740-battery @@ -41,7 +46,7 @@ required: - io-channel-names - monitored-battery -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/regulator/act8865-regulator.txt b/Documentation/devicetree/bindings/regulator/act8865-regulator.txt deleted file mode 100644 index b9f58e48034989..00000000000000 --- a/Documentation/devicetree/bindings/regulator/act8865-regulator.txt +++ /dev/null @@ -1,117 +0,0 @@ -ACT88xx regulators -------------------- - -Required properties: -- compatible: "active-semi,act8846" or "active-semi,act8865" or "active-semi,act8600" -- reg: I2C slave address - -Optional properties: -- system-power-controller: Telling whether or not this pmic is controlling - the system power. See Documentation/devicetree/bindings/power/power-controller.txt . -- active-semi,vsel-high: Indicates the VSEL pin is high. - If this property is missing, assume the VSEL pin is low(0). - -Optional input supply properties: -- for act8600: - - vp1-supply: The input supply for DCDC_REG1 - - vp2-supply: The input supply for DCDC_REG2 - - vp3-supply: The input supply for DCDC_REG3 - - inl-supply: The input supply for LDO_REG5, LDO_REG6, LDO_REG7 and LDO_REG8 - SUDCDC_REG4, LDO_REG9 and LDO_REG10 do not have separate supplies. -- for act8846: - - vp1-supply: The input supply for REG1 - - vp2-supply: The input supply for REG2 - - vp3-supply: The input supply for REG3 - - vp4-supply: The input supply for REG4 - - inl1-supply: The input supply for REG5, REG6 and REG7 - - inl2-supply: The input supply for REG8 and LDO_REG9 - - inl3-supply: The input supply for REG10, REG11 and REG12 -- for act8865: - - vp1-supply: The input supply for DCDC_REG1 - - vp2-supply: The input supply for DCDC_REG2 - - vp3-supply: The input supply for DCDC_REG3 - - inl45-supply: The input supply for LDO_REG1 and LDO_REG2 - - inl67-supply: The input supply for LDO_REG3 and LDO_REG4 - -Any standard regulator properties can be used to configure the single regulator. -regulator-initial-mode, regulator-allowed-modes and regulator-mode could be specified -for act8865 using mode values from dt-bindings/regulator/active-semi,8865-regulator.h -file. - -The valid names for regulators are: - - for act8846: - REG1, REG2, REG3, REG4, REG5, REG6, REG7, REG8, REG9, REG10, REG11, REG12 - - for act8865: - DCDC_REG1, DCDC_REG2, DCDC_REG3, LDO_REG1, LDO_REG2, LDO_REG3, LDO_REG4. - - for act8600: - DCDC_REG1, DCDC_REG2, DCDC_REG3, SUDCDC_REG4, LDO_REG5, LDO_REG6, LDO_REG7, - LDO_REG8, LDO_REG9, LDO_REG10, - -Example: --------- - -#include - - i2c1: i2c@f0018000 { - pmic: act8865@5b { - compatible = "active-semi,act8865"; - reg = <0x5b>; - active-semi,vsel-high; - - regulators { - vcc_1v8_reg: DCDC_REG1 { - regulator-name = "VCC_1V8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - vcc_1v2_reg: DCDC_REG2 { - regulator-name = "VCC_1V2"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1300000>; - regulator-always-on; - - regulator-allowed-modes = , - ; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-min-microvolt = <1150000>; - regulator-suspend-max-microvolt = <1150000>; - regulator-changeable-in-suspend; - regulator-mode = ; - }; - }; - - vcc_3v3_reg: DCDC_REG3 { - regulator-name = "VCC_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - vddana_reg: LDO_REG1 { - regulator-name = "VDDANA"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - - regulator-allowed-modes = , - ; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vddfuse_reg: LDO_REG2 { - regulator-name = "FUSE_2V5"; - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <2500000>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt deleted file mode 100644 index 4017527619ab2d..00000000000000 --- a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt +++ /dev/null @@ -1,113 +0,0 @@ -Device-Tree bindings for regulators of Active-semi ACT8945A Multi-Function Device - -Required properties: - - compatible: "active-semi,act8945a", please refer to ../mfd/act8945a.txt. - -Optional properties: -- active-semi,vsel-high: Indicates if the VSEL pin is set to logic-high. - If this property is missing, assume the VSEL pin is set to logic-low. - -Optional input supply properties: - - vp1-supply: The input supply for REG_DCDC1 - - vp2-supply: The input supply for REG_DCDC2 - - vp3-supply: The input supply for REG_DCDC3 - - inl45-supply: The input supply for REG_LDO1 and REG_LDO2 - - inl67-supply: The input supply for REG_LDO3 and REG_LDO4 - -Any standard regulator properties can be used to configure the single regulator. -regulator-initial-mode, regulator-allowed-modes and regulator-mode could be -specified using mode values from dt-bindings/regulator/active-semi,8945a-regulator.h -file. - -The valid names for regulators are: - REG_DCDC1, REG_DCDC2, REG_DCDC3, REG_LDO1, REG_LDO2, REG_LDO3, REG_LDO4. - -Example: - -#include - - pmic@5b { - compatible = "active-semi,act8945a"; - reg = <0x5b>; - - active-semi,vsel-high; - - regulators { - vdd_1v35_reg: REG_DCDC1 { - regulator-name = "VDD_1V35"; - regulator-min-microvolt = <1350000>; - regulator-max-microvolt = <1350000>; - regulator-always-on; - - regulator-allowed-modes = , - ; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-min-microvolt=<1400000>; - regulator-suspend-max-microvolt=<1400000>; - regulator-changeable-in-suspend; - regulator-mode=; - }; - }; - - vdd_1v2_reg: REG_DCDC2 { - regulator-name = "VDD_1V2"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1300000>; - regulator-always-on; - - regulator-allowed-modes = , - ; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_3v3_reg: REG_DCDC3 { - regulator-name = "VDD_3V3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - vdd_fuse_reg: REG_LDO1 { - regulator-name = "VDD_FUSE"; - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <2500000>; - regulator-always-on; - - regulator-allowed-modes = , - ; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vdd_3v3_lp_reg: REG_LDO2 { - regulator-name = "VDD_3V3_LP"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - vdd_led_reg: REG_LDO3 { - regulator-name = "VDD_LED"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - vdd_sdhc_1v8_reg: REG_LDO4 { - regulator-name = "VDD_SDHC_1V8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/regulator/active-semi,act8600.yaml b/Documentation/devicetree/bindings/regulator/active-semi,act8600.yaml new file mode 100644 index 00000000000000..d8cc9cd527efc8 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/active-semi,act8600.yaml @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/active-semi,act8600.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Active-semi ACT8600 regulator + +maintainers: + - Paul Cercueil + +properties: + compatible: + const: active-semi,act8600 + + reg: + description: I2C address + maxItems: 1 + + system-power-controller: + description: + Indicates that the ACT8600 is responsible for powering OFF + the system. + type: boolean + + active-semi,vsel-high: + description: + Indicates the VSEL pin is high. If this property is missing, + the VSEL pin is assumed to be low. + type: boolean + + regulators: + type: object + additionalProperties: false + + properties: + DCDC1: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp1-supply: + description: Handle to the VP1 input supply + + DCDC2: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp2-supply: + description: Handle to the VP2 input supply + + DCDC3: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp3-supply: + description: Handle to the VP3 input supply + + patternProperties: + "^(SUDCDC_REG4|LDO_REG9|LDO_REG10)$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + "^LDO[5-8]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl-supply: + description: Handle to the INL input supply + +additionalProperties: false + +required: + - reg + - compatible + - regulators + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@5a { + compatible = "active-semi,act8600"; + reg = <0x5a>; + + regulators { + SUDCDC_REG4 { + regulator-min-microvolt = <5300000>; + regulator-max-microvolt = <5300000>; + inl-supply = <&vcc>; + }; + + LDO5 { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + inl-supply = <&vcc>; + }; + + LDO6 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + inl-supply = <&vcc>; + }; + + LDO7 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + inl-supply = <&vcc>; + }; + + LDO8 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + inl-supply = <&vcc>; + }; + + LDO_REG9 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + inl-supply = <&vcc>; + }; + + LDO_REG10 { + inl-supply = <&vcc>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/active-semi,act8846.yaml b/Documentation/devicetree/bindings/regulator/active-semi,act8846.yaml new file mode 100644 index 00000000000000..f276dec59b3d17 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/active-semi,act8846.yaml @@ -0,0 +1,206 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/active-semi,act8846.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Active-semi ACT8846 regulator + +maintainers: + - Paul Cercueil + +properties: + compatible: + const: active-semi,act8846 + + reg: + description: I2C address + maxItems: 1 + + system-power-controller: + description: + Indicates that the ACT8846 is responsible for powering OFF + the system. + type: boolean + + active-semi,vsel-high: + description: + Indicates the VSEL pin is high. If this property is missing, + the VSEL pin is assumed to be low. + type: boolean + + regulators: + type: object + additionalProperties: false + + properties: + REG1: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp1-supply: + description: Handle to the VP1 input supply + + REG2: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp2-supply: + description: Handle to the VP2 input supply + + REG3: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp3-supply: + description: Handle to the VP3 input supply + + REG4: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp4-supply: + description: Handle to the VP4 input supply + + patternProperties: + "^REG[5-7]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl1-supply: + description: Handle to the INL1 input supply + + "^REG[8-9]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl2-supply: + description: Handle to the INL2 input supply + + "^REG1[0-2]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl3-supply: + description: Handle to the INL3 input supply + +additionalProperties: false + +required: + - reg + - compatible + - regulators + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@5a { + compatible = "active-semi,act8846"; + reg = <0x5a>; + + system-power-controller; + + regulators { + REG1 { + regulator-name = "VCC_DDR"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + }; + + REG2 { + regulator-name = "VCC_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + REG3 { + regulator-name = "VDD_LOG"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + REG4 { + regulator-name = "VCC_20"; + regulator-min-microvolt = <2000000>; + regulator-max-microvolt = <2000000>; + regulator-always-on; + }; + + REG5 { + regulator-name = "VCCIO_SD"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + REG6 { + regulator-name = "VDD10_LCD"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + REG7 { + regulator-name = "VCC_WL"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + REG8 { + regulator-name = "VCCA_33"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + REG9 { + regulator-name = "VCC_LAN"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + REG10 { + regulator-name = "VDD_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + REG11 { + regulator-name = "VCC_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + REG12 { + regulator-name = "VCC18_LCD"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/active-semi,act8865.yaml b/Documentation/devicetree/bindings/regulator/active-semi,act8865.yaml new file mode 100644 index 00000000000000..cf36ab7c82c4d9 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/active-semi,act8865.yaml @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/active-semi,act8865.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Active-semi ACT8865 regulator + +maintainers: + - Liam Girdwood + - Mark Brown + +properties: + compatible: + const: active-semi,act8865 + + reg: + description: I2C address + maxItems: 1 + + system-power-controller: + description: | + Indicates that the ACT8865 is responsible for powering OFF + the system. + type: boolean + + active-semi,vsel-high: + description: | + Indicates the VSEL pin is high. If this property is missing, + the VSEL pin is assumed to be low. + type: boolean + + regulators: + type: object + unevaluatedProperties: false + + properties: + DCDC_REG1: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp1-supply: + description: Handle to the VP1 input supply + + DCDC_REG2: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp2-supply: + description: Handle to the VP2 input supply + + DCDC_REG3: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp3-supply: + description: Handle to the VP3 input supply + + patternProperties: + "^LDO_REG[1-2]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl45-supply: + description: Handle to the INL45 input supply + + "^LDO_REG[3-4]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl67-supply: + description: Handle to the INL67 input supply + + additionalProperties: false + +additionalProperties: false + +required: + - reg + - compatible + - regulators + +examples: + - | + #include + + i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + pmic: act8865@5b { + compatible = "active-semi,act8865"; + reg = <0x5b>; + active-semi,vsel-high; + + regulators { + vcc_1v8_reg: DCDC_REG1 { + regulator-name = "VCC_1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vcc_1v2_reg: DCDC_REG2 { + regulator-name = "VCC_1V2"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-min-microvolt = <1150000>; + regulator-suspend-max-microvolt = <1150000>; + regulator-changeable-in-suspend; + regulator-mode = ; + }; + }; + + vcc_3v3_reg: DCDC_REG3 { + regulator-name = "VCC_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vddana_reg: LDO_REG1 { + regulator-name = "VDDANA"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vddfuse_reg: LDO_REG2 { + regulator-name = "FUSE_2V5"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/regulator/active-semi,act8945a.yaml b/Documentation/devicetree/bindings/regulator/active-semi,act8945a.yaml new file mode 100644 index 00000000000000..b8c0ba8247efa7 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/active-semi,act8945a.yaml @@ -0,0 +1,259 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/active-semi,act8945a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Active-semi ACT8945a regulator + +maintainers: + - Paul Cercueil + +properties: + compatible: + const: active-semi,act8945a + + reg: + description: I2C address + maxItems: 1 + + system-power-controller: + description: + Indicates that the ACT8945a is responsible for powering OFF + the system. + type: boolean + + active-semi,vsel-high: + description: + Indicates the VSEL pin is high. If this property is missing, + the VSEL pin is assumed to be low. + type: boolean + + regulators: + type: object + additionalProperties: false + + properties: + REG_DCDC1: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp1-supply: + description: Handle to the VP1 input supply + + REG_DCDC2: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp2-supply: + description: Handle to the VP2 input supply + + REG_DCDC3: + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + vp3-supply: + description: Handle to the VP3 input supply + + patternProperties: + "^REG_LDO[1-2]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl45-supply: + description: Handle to the INL45 input supply + + "^REG_LDO[3-4]$": + type: object + $ref: /schemas/regulator/regulator.yaml# + unevaluatedProperties: false + + properties: + inl67-supply: + description: Handle to the INL67 input supply + + charger: + type: object + additionalProperties: false + + properties: + compatible: + const: active-semi,act8945a-charger + + interrupts: + maxItems: 1 + + active-semi,chglev-gpios: + description: CGHLEV GPIO + maxItems: 1 + + active-semi,lbo-gpios: + description: LBO GPIO + maxItems: 1 + + active-semi,input-voltage-threshold-microvolt: + description: Input voltage threshold + maxItems: 1 + + active-semi,precondition-timeout: + description: Precondition timeout + $ref: /schemas/types.yaml#/definitions/uint32 + + active-semi,total-timeout: + description: Total timeout + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - compatible + - interrupts + +additionalProperties: false + +required: + - reg + - compatible + - regulators + +examples: + - | + #include + #include + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@5b { + compatible = "active-semi,act8945a"; + reg = <0x5b>; + active-semi,vsel-high; + + regulators { + REG_DCDC1 { + regulator-name = "VDD_1V35"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-min-microvolt = <1400000>; + regulator-suspend-max-microvolt = <1400000>; + regulator-changeable-in-suspend; + regulator-mode = ; + }; + }; + + REG_DCDC2 { + regulator-name = "VDD_1V2"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1300000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + REG_DCDC3 { + regulator-name = "VDD_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + REG_LDO1 { + regulator-name = "VDD_FUSE"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + REG_LDO2 { + regulator-name = "VDD_3V3_LP"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + REG_LDO3 { + regulator-name = "VDD_LED"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + REG_LDO4 { + regulator-name = "VDD_SDHC_1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + + charger { + compatible = "active-semi,act8945a-charger"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_charger_chglev &pinctrl_charger_lbo &pinctrl_charger_irq>; + interrupt-parent = <&pioA>; + interrupts = <45 IRQ_TYPE_EDGE_RISING>; + + active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>; + active-semi,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>; + active-semi,input-voltage-threshold-microvolt = <6600>; + active-semi,precondition-timeout = <40>; + active-semi,total-timeout = <3>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml index aaaaabad46ea6e..d2d219f853b1fe 100644 --- a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml @@ -17,7 +17,10 @@ maintainers: properties: compatible: - const: ingenic,jz4770-vpu-rproc + items: + - enum: + - ingenic,jz4770-vpu-rproc + - const: simple-mfd reg: items: @@ -54,6 +57,10 @@ required: - clock-names - interrupts +patternProperties: + "^hwlock@[a-f0-9]+$": + allOf: [ $ref: "../hwlock/ingenic,vpu-hwspinlock.yaml" ] + additionalProperties: false examples: @@ -61,7 +68,10 @@ examples: #include vpu: video-decoder@132a0000 { - compatible = "ingenic,jz4770-vpu-rproc"; + compatible = "ingenic,jz4770-vpu-rproc", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x132a0000 0x20>; reg = <0x132a0000 0x20>, /* AUX */ <0x132b0000 0x4000>, /* TCSM0 */ @@ -74,4 +84,12 @@ examples: interrupt-parent = <&cpuintc>; interrupts = <3>; + + hwlock: hwlock@4 { + compatible = "ingenic,jz4770-vpu-hwspinlock", + "ingenic,jz4755-vpu-hwspinlock"; + reg = <0x4 0xc>; + + #hwlock-cells = <1>; + }; }; diff --git a/Documentation/devicetree/bindings/rtc/ingenic,rtc.yaml b/Documentation/devicetree/bindings/rtc/ingenic,rtc.yaml index b235b2441997f4..d63bb727cee50b 100644 --- a/Documentation/devicetree/bindings/rtc/ingenic,rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/ingenic,rtc.yaml @@ -11,6 +11,16 @@ maintainers: allOf: - $ref: rtc.yaml# + - if: + not: + properties: + compatible: + contains: + enum: + - ingenic,jz4770-rtc + then: + properties: + "#clock-cells": false properties: compatible: @@ -18,14 +28,14 @@ properties: - enum: - ingenic,jz4740-rtc - ingenic,jz4760-rtc + - ingenic,jz4770-rtc - items: - const: ingenic,jz4725b-rtc - const: ingenic,jz4740-rtc - items: - enum: - - ingenic,jz4770-rtc - ingenic,jz4780-rtc - - const: ingenic,jz4760-rtc + - const: ingenic,jz4770-rtc reg: maxItems: 1 @@ -39,6 +49,9 @@ properties: clock-names: const: rtc + "#clock-cells": + const: 0 + system-power-controller: description: | Indicates that the RTC is responsible for powering OFF @@ -83,3 +96,18 @@ examples: clocks = <&cgu JZ4740_CLK_RTC>; clock-names = "rtc"; }; + + - | + #include + rtc: rtc@10003000 { + compatible = "ingenic,jz4780-rtc", "ingenic,jz4770-rtc"; + reg = <0x10003000 0x4c>; + + interrupt-parent = <&intc>; + interrupts = <32>; + + clocks = <&cgu JZ4780_CLK_RTCLK>; + clock-names = "rtc"; + + #clock-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 6e323a380294ad..49561a589f21e6 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1448,6 +1448,8 @@ patternProperties: description: Wobo "^wanchanglong,.*": description: Wanchanglong Electronics Technology(SHENZHEN)Co.,Ltd. + "^wolsen,.*": + description: Shenzhen Wolsen Technology Co., Ltd. "^x-powers,.*": description: X-Powers "^xen,.*": diff --git a/arch/arm/configs/clps711x_defconfig b/arch/arm/configs/clps711x_defconfig index 92481b2a88fa24..adcee238822a3d 100644 --- a/arch/arm/configs/clps711x_defconfig +++ b/arch/arm/configs/clps711x_defconfig @@ -14,7 +14,8 @@ CONFIG_ARCH_EDB7211=y CONFIG_ARCH_P720T=y CONFIG_AEABI=y # CONFIG_COREDUMP is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/configs/collie_defconfig b/arch/arm/configs/collie_defconfig index 2a2d2cb3ce2ea7..69341c33e0cc67 100644 --- a/arch/arm/configs/collie_defconfig +++ b/arch/arm/configs/collie_defconfig @@ -13,7 +13,8 @@ CONFIG_CMDLINE="noinitrd root=/dev/mtdblock2 rootfstype=jffs2 fbcon=rotate:1" CONFIG_FPE_NWFPE=y CONFIG_PM=y # CONFIG_SWAP is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/configs/multi_v4t_defconfig b/arch/arm/configs/multi_v4t_defconfig index e2fd822f741a83..b60000a89affd8 100644 --- a/arch/arm/configs/multi_v4t_defconfig +++ b/arch/arm/configs/multi_v4t_defconfig @@ -25,7 +25,8 @@ CONFIG_ARM_CLPS711X_CPUIDLE=y CONFIG_JUMP_LABEL=y CONFIG_PARTITION_ADVANCED=y # CONFIG_COREDUMP is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index 70511fe4b3ecfa..246f1bba7df52c 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -42,7 +42,8 @@ CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_PARTITION_ADVANCED=y CONFIG_BINFMT_MISC=y # CONFIG_SWAP is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_NET=y CONFIG_PACKET=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index d60cc9cc4c2140..0a0f12df40b564 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -49,7 +49,8 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_LDM_PARTITION=y CONFIG_CMDLINE_PARTITION=y CONFIG_BINFMT_MISC=y -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_COMPACTION is not set CONFIG_NET=y CONFIG_PACKET=y diff --git a/arch/arm/configs/tct_hammer_defconfig b/arch/arm/configs/tct_hammer_defconfig index 3b29ae1fb75022..6bd38b6f22c411 100644 --- a/arch/arm/configs/tct_hammer_defconfig +++ b/arch/arm/configs/tct_hammer_defconfig @@ -19,7 +19,8 @@ CONFIG_FPE_NWFPE=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_SWAP is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/configs/xcep_defconfig b/arch/arm/configs/xcep_defconfig index ea59e4b6bfc5e7..6bd9f71b71fc8d 100644 --- a/arch/arm/configs/xcep_defconfig +++ b/arch/arm/configs/xcep_defconfig @@ -26,7 +26,8 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y # CONFIG_BLOCK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_COMPAT_BRK is not set # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_NET=y diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index b26b77673c2cc3..03157122281fd3 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -9,7 +9,6 @@ config MIPS select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE if !EVA - select ARCH_HAS_PTE_SPECIAL if !(32BIT && CPU_HAS_RIXI) select ARCH_HAS_STRNCPY_FROM_USER select ARCH_HAS_STRNLEN_USER select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST @@ -23,6 +22,7 @@ config MIPS select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS select ARCH_SUPPORTS_HUGETLBFS if CPU_SUPPORTS_HUGEPAGES + select ARCH_SUPPORTS_LTO_CLANG if CPU_LITTLE_ENDIAN select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_LD_ORPHAN_WARN @@ -114,6 +114,7 @@ config MACH_INGENIC select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_SUPPORTS_ZBOOT + select CPU_SUPPORTS_HUGEPAGES select DMA_NONCOHERENT select ARCH_HAS_SYNC_DMA_FOR_CPU select IRQ_MIPS_CPU @@ -1974,7 +1975,7 @@ config CPU_MIPSR1 config CPU_MIPSR2 bool default y if CPU_MIPS32_R2 || CPU_MIPS64_R2 || CPU_CAVIUM_OCTEON - select CPU_HAS_RIXI + #select CPU_HAS_RIXI select CPU_HAS_DIEI if !CPU_DIEI_BROKEN select MIPS_SPRAM diff --git a/arch/mips/Makefile b/arch/mips/Makefile index b296e33f8e333a..f16e08614a4270 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -216,8 +216,10 @@ ifeq ($(CONFIG_CPU_HAS_MSA),y) toolchain-msa := $(call cc-option-yn,$(mips-cflags) -mhard-float -mfp64 -Wa$(comma)-mmsa) cflags-$(toolchain-msa) += -DTOOLCHAIN_SUPPORTS_MSA endif +ifneq ($(CONFIG_CC_IS_CLANG),y) toolchain-virt := $(call cc-option-yn,$(mips-cflags) -mvirt) cflags-$(toolchain-virt) += -DTOOLCHAIN_SUPPORTS_VIRT +endif # For -mmicromips, use -Wa,-fatal-warnings to catch unsupported -mxpa which # only warns xpa-cflags-y := $(mips-cflags) diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile index 6cc28173bee894..f6c45b2246fdab 100644 --- a/arch/mips/boot/compressed/Makefile +++ b/arch/mips/boot/compressed/Makefile @@ -27,10 +27,13 @@ ifdef CONFIG_CPU_LOONGSON64 KBUILD_CFLAGS := $(filter-out -march=loongson3a, $(KBUILD_CFLAGS)) -march=mips64r2 endif -KBUILD_CFLAGS := $(KBUILD_CFLAGS) -D__KERNEL__ -D__DISABLE_EXPORTS \ +# Disable LTO +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS)) + +KBUILD_CFLAGS := $(KBUILD_CFLAGS) -mno-abicalls -D__KERNEL__ -D__DISABLE_EXPORTS \ -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) -D"VMLINUX_LOAD_ADDRESS_ULL=$(VMLINUX_LOAD_ADDRESS)ull" -KBUILD_AFLAGS := $(KBUILD_AFLAGS) -D__ASSEMBLY__ \ +KBUILD_AFLAGS := $(KBUILD_AFLAGS) -mno-abicalls -D__ASSEMBLY__ \ -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) \ -DKERNEL_ENTRY=$(VMLINUX_ENTRY_ADDRESS) diff --git a/arch/mips/boot/dts/ingenic/Makefile b/arch/mips/boot/dts/ingenic/Makefile index 54aa0c4e609175..e2aa2f25783ded 100644 --- a/arch/mips/boot/dts/ingenic/Makefile +++ b/arch/mips/boot/dts/ingenic/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_JZ4740_QI_LB60) += qi_lb60.dtb dtb-$(CONFIG_JZ4740_RS90) += rs90.dtb +dtb-$(CONFIG_JZ4760_RS97) += rs97.dtb dtb-$(CONFIG_JZ4770_GCW0) += gcw0.dtb dtb-$(CONFIG_JZ4780_CI20) += ci20.dtb dtb-$(CONFIG_X1000_CU1000_NEO) += cu1000-neo.dtb diff --git a/arch/mips/boot/dts/ingenic/bits/gcw0-base.dtsi b/arch/mips/boot/dts/ingenic/bits/gcw0-base.dtsi new file mode 100644 index 00000000000000..7914276a612eaa --- /dev/null +++ b/arch/mips/boot/dts/ingenic/bits/gcw0-base.dtsi @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "../jz4770.dtsi" + +#include +#include +#include +#include + +/ { + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + }; + + memory: memory { + device_type = "memory"; + reg = <0x0 0x10000000>, + <0x30000000 0x10000000>; + }; + + chosen { + stdout-path = "serial2:57600n8"; + }; + + vcc: regulator-0 { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + mmc1_power: regulator-1 { + compatible = "regulator-fixed"; + regulator-name = "mmc1_vcc"; + gpio = <&gpe 9 0>; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc>; + }; + + headphones_amp: analog-amplifier-0 { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpf 3 GPIO_ACTIVE_LOW>; + enable-delay-ms = <50>; + + VCC-supply = <&vcc>; + sound-name-prefix = "Headphones Amp"; + }; + + speaker_amp: analog-amplifier-1 { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpf 20 GPIO_ACTIVE_HIGH>; + + VCC-supply = <&vcc>; + sound-name-prefix = "Speaker Amp"; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + + simple-audio-card,name = "gcw0-audio"; + simple-audio-card,format = "i2s"; + + simple-audio-card,hp-det-gpio = <&gpf 21 GPIO_ACTIVE_LOW>; + simple-audio-card,aux-devs = <&speaker_amp>, <&headphones_amp>; + + simple-audio-card,bitclock-master = <&dai_codec>; + simple-audio-card,frame-master = <&dai_codec>; + + dai_cpu: simple-audio-card,cpu { + sound-dai = <&aic>; + }; + + dai_codec: simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 1 40000 0>; + power-supply = <&vcc>; + + brightness-levels = <0 4 8 9 10 12 16 32 64 96 128 192 255>; + default-brightness-level = <8>; + + pinctrl-names = "init", "default"; + pinctrl-0 = <&pins_pwm1_sleep>; + pinctrl-1 = <&pins_pwm1>; + }; + + gpio_keys: gpio-keys { + compatible = "gpio-keys"; + autorepeat; + + button-0 { + label = "D-pad up"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 21 GPIO_ACTIVE_LOW>; + }; + + button-1 { + label = "D-pad down"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 25 GPIO_ACTIVE_LOW>; + }; + + button-2 { + label = "D-pad left"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 23 GPIO_ACTIVE_LOW>; + }; + + button-3 { + label = "D-pad right"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 24 GPIO_ACTIVE_LOW>; + }; + + button-4 { + label = "Button A"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 29 GPIO_ACTIVE_LOW>; + }; + + button-5 { + label = "Button B"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 20 GPIO_ACTIVE_LOW>; + }; + + btn6: button-6 { + label = "Button X"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 27 GPIO_ACTIVE_LOW>; + }; + + btn7: button-7 { + label = "Button Y"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 28 GPIO_ACTIVE_LOW>; + }; + + btn8: button-8 { + label = "Left shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 20 GPIO_ACTIVE_LOW>; + }; + + btn9: button-9 { + label = "Right shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 26 GPIO_ACTIVE_LOW>; + }; + + button-10 { + label = "Start button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 21 GPIO_ACTIVE_LOW>; + }; + + button-11 { + label = "Select button"; + linux,code = ; + linux,can-disable; + /* + * This is the only button that is active high, + * since it doubles as BOOT_SEL1. + */ + gpios = <&gpd 18 GPIO_ACTIVE_HIGH>; + }; + + btn12: button-12 { + label = "Power button"; + linux,code = ; + linux,can-disable; + gpios = <&gpa 30 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + /* + * SPI-over-GPIO, using the pins of the SSI0 controller. + * This is useful for the devices that require 3-wire communication, + * as the SSI controller in Ingenic SoCs does not support it. + */ + spi0_gpio: spi { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + sck-gpios = <&gpe 15 GPIO_ACTIVE_HIGH>; + mosi-gpios = <&gpe 17 GPIO_ACTIVE_HIGH>; + cs-gpios = <&gpe 16 GPIO_ACTIVE_HIGH>; + num-chipselects = <1>; + }; + + usb_conn: connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + label = "mini-USB"; + type = "mini"; + + /* + * USB OTG is not yet working reliably, the ID detection + * mechanism tends to fry easily for unknown reasons. + * Until this is fixed, disable OTG by not providing the + * ID GPIO to the driver. + */ + //id-gpios = <&gpf 18 GPIO_ACTIVE_LOW>; + + vbus-gpios = <&gpb 5 GPIO_ACTIVE_HIGH>; + vbus-supply = <&vcc>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_otg>; + + port { + usb_ep: endpoint { + remote-endpoint = <&usb_otg_ep>; + }; + }; + }; + + battery: battery { + compatible = "simple-battery"; + voltage-min-design-microvolt = <3400000>; + voltage-max-design-microvolt = <4200000>; + }; + + vdiv: voltage-divider { + compatible = "voltage-divider"; + #io-channel-cells = <0>; + + output-ohms = <332000>; + full-ohms = <1332000>; + + io-channels = <&adc INGENIC_ADC_BATTERY>; + }; + + pmu: pmu { + compatible = "ingenic,jz4770-battery", "ingenic,jz4740-battery"; + io-channels = <&vdiv>; + io-channel-names = "battery"; + monitored-battery = <&battery>; + + power-supplies = <&usb_conn>; + }; + + joystick: joystick { + compatible = "adc-joystick"; + io-channels = <&adc INGENIC_ADC_TOUCH_XP>, + <&adc INGENIC_ADC_TOUCH_YP>; + #address-cells = <1>; + #size-cells = <0>; + + js_axis0: axis@0 { + reg = <0>; + linux,code = ; + abs-fuzz = <4>; + abs-flat = <200>; + }; + + js_axis1: axis@1 { + reg = <1>; + linux,code = ; + abs-fuzz = <4>; + abs-flat = <200>; + }; + }; + + leds: leds { + compatible = "gpio-leds"; + + power_led: led { + label = "power"; + gpios = <&gpb 30 GPIO_ACTIVE_LOW>; + default-state = "on"; + panic-indicator; + retain-state-suspended; + }; + }; + + cpu_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-444000000 { opp-hz = /bits/ 64 <444000000>; }; + opp-492000000 { opp-hz = /bits/ 64 <492000000>; }; + opp-600000000 { opp-hz = /bits/ 64 <600000000>; }; + opp-696000000 { opp-hz = /bits/ 64 <696000000>; }; + opp-792000000 { opp-hz = /bits/ 64 <792000000>; }; + opp-900000000 { opp-hz = /bits/ 64 <900000000>; }; + opp-996000000 { opp-hz = /bits/ 64 <996000000>; }; + opp-1092000000 { opp-hz = /bits/ 64 <1092000000>; }; + opp-1200000000 { opp-hz = /bits/ 64 <1200000000>; }; + }; +}; + +&ext { + clock-frequency = <12000000>; +}; + +&cpu0 { + operating-points-v2 = <&cpu_opp_table>; + + /* We use the main PLL as the CPU clock for the cpufreq driver. */ + clocks = <&cgu JZ4770_CLK_PLL0>; +}; + +&pinctrl { + pins_lcd: lcd { + function = "lcd"; + groups = "lcd-24bit"; + }; + + pins_uart2: uart2 { + function = "uart2"; + groups = "uart2-data"; + }; + + pins_mmc0: mmc0 { + function = "mmc0"; + groups = "mmc0-1bit-a", "mmc0-4bit-a"; + }; + + pins_mmc1: mmc1 { + function = "mmc1"; + groups = "mmc1-1bit-d", "mmc1-4bit-d"; + }; + + pins_otg: otg { + otg-vbus-pin { + function = "otg"; + groups = "otg-vbus"; + }; + + vbus-pin { + pins = "PB5"; + bias-disable; + }; + }; + + pwm1 { + pins_pwm1_sleep: pwm1-sleep { + pins = "PE1"; + output-low; + }; + + pins_pwm1: pwm1-default { + function = "pwm1"; + groups = "pwm1"; + }; + }; +}; + +&lcd { + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pins_uart2>; + + status = "okay"; +}; + +&cgu { + /* + * Put high-speed peripherals under PLL1, such that we can change the + * PLL0 frequency on demand without having to suspend peripherals. + * We use a rate of 432 MHz, which is the least common multiple of + * 27 MHz (required by TV encoder) and 48 MHz (required by USB host). + * Put the GPU under PLL0 since we want a higher frequency. + * Use the 32 kHz oscillator as the parent of the RTC for a higher + * precision. + */ + assigned-clocks = + <&cgu JZ4770_CLK_PLL1>, + <&cgu JZ4770_CLK_GPU>, + <&cgu JZ4770_CLK_RTC>, + <&cgu JZ4770_CLK_UHC>, + <&cgu JZ4770_CLK_LPCLK_MUX>, + <&cgu JZ4770_CLK_MMC0_MUX>, + <&cgu JZ4770_CLK_MMC1_MUX>; + assigned-clock-parents = + <0>, + <&cgu JZ4770_CLK_PLL0>, + <&cgu JZ4770_CLK_OSC32K>, + <&cgu JZ4770_CLK_PLL1>, + <&cgu JZ4770_CLK_PLL1>, + <&cgu JZ4770_CLK_PLL1>, + <&cgu JZ4770_CLK_PLL1>; + assigned-clock-rates = + <432000000>, + <600000000>; +}; + +&tcu { + /* + * 750 kHz for the system timer and clocksource, 12 MHz for the OST, + * and use RTC as the parent for the watchdog clock + */ + assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>, + <&tcu TCU_CLK_OST>, <&tcu TCU_CLK_WDT>; + assigned-clock-parents = <0>, <0>, <0>, <&cgu JZ4770_CLK_RTC>; + assigned-clock-rates = <750000>, <750000>, <12000000>; + + /* PWM1 is in use, so use channel #2 for the clocksource */ + ingenic,pwm-channels-mask = <0xfa>; +}; + +&usb_otg { + port { + usb_otg_ep: endpoint { + remote-endpoint = <&usb_ep>; + }; + }; +}; + +&otg_phy { + vcc-supply = <&vcc>; +}; + +&rtc { + clocks = <&cgu JZ4770_CLK_RTC>; + clock-names = "rtc"; + + system-power-controller; + + ingenic,reset-pin-assert-time-ms = <125>; + ingenic,min-wakeup-pin-assert-time-ms = <500>; +}; + +&mmc0 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + vmmc-supply = <&vcc>; + non-removable; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc0>; +}; + +&mmc1 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + cd-gpios = <&gpb 2 GPIO_ACTIVE_LOW>; + vmmc-supply = <&mmc1_power>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc1>; +}; diff --git a/arch/mips/boot/dts/ingenic/bits/rs90-base.dtsi b/arch/mips/boot/dts/ingenic/bits/rs90-base.dtsi new file mode 100644 index 00000000000000..68894fb0096a5f --- /dev/null +++ b/arch/mips/boot/dts/ingenic/bits/rs90-base.dtsi @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "../jz4725b.dtsi" + +#include +#include +#include + +/ { + + memory { + device_type = "memory"; + reg = <0x0 0x2000000>; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + vmem: video-memory@1f00000 { + compatible = "shared-dma-pool"; + reg = <0x1f00000 0x100000>; + reusable; + }; + }; + + vcc: regulator { + compatible = "regulator-fixed"; + + regulator-name = "vcc"; + regulaor-min-microvolt = <3300000>; + regulaor-max-microvolt = <3300000>; + regulator-always-on; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 3 40000 0>; + + brightness-levels = <0 16 32 48 64 80 112 144 192 255>; + default-brightness-level = <8>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_pwm3>; + + power-supply = <&vcc>; + }; + + amp: analog-amplifier { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpc 15 GPIO_ACTIVE_HIGH>; + + VCC-supply = <&vcc>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + + simple-audio-card,name = "rs90-audio"; + simple-audio-card,format = "i2s"; + + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + simple-audio-card,routing = + "INL", "LHPOUT", + "INR", "RHPOUT", + "Headphones", "LHPOUT", + "Headphones", "RHPOUT", + "Speaker", "OUTL", + "Speaker", "OUTR"; + simple-audio-card,pin-switches = "Speaker"; + + simple-audio-card,hp-det-gpio = <&gpd 16 GPIO_ACTIVE_LOW>; + simple-audio-card,aux-devs = <&>; + + simple-audio-card,bitclock-master = <&dai_codec>; + simple-audio-card,frame-master = <&dai_codec>; + + dai_cpu: simple-audio-card,cpu { + sound-dai = <&aic>; + }; + + dai_codec: simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; + + usb_phy: usb-phy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0>; + + clocks = <&cgu JZ4725B_CLK_UDC_PHY>; + clock-names = "main_clk"; + vcc-supply = <&vcc>; + }; + + battery: battery { + compatible = "simple-battery"; + voltage-min-design-microvolt = <3692000>; + voltage-max-design-microvolt = <3900000>; + charge-full-design-microamp-hours = <1020000>; + }; + + resistor: resistor { + compatible = "voltage-divider"; + #io-channel-cells = <0>; + + output-ohms = <1125>; + full-ohms = <3900>; + + io-channels = <&adc INGENIC_ADC_BATTERY>; + }; + + pmu: pmu { + compatible = "ingenic,jz4725b-battery", "ingenic,jz4740-battery"; + io-channels = <&resistor>; + io-channel-names = "battery"; + monitored-battery = <&battery>; + }; + + cpu_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-216000000 { opp-hz = /bits/ 64 <216000000>; }; + opp-300000000 { opp-hz = /bits/ 64 <300000000>; }; + opp-336000000 { opp-hz = /bits/ 64 <336000000>; }; + opp-360000000 { opp-hz = /bits/ 64 <360000000>; }; + opp-378000000 { opp-hz = /bits/ 64 <378000000>; }; + opp-396000000 { opp-hz = /bits/ 64 <396000000>; }; + opp-420000000 { opp-hz = /bits/ 64 <420000000>; }; + opp-438000000 { opp-hz = /bits/ 64 <438000000>; }; + opp-456000000 { opp-hz = /bits/ 64 <456000000>; }; + }; +}; + +&ext { + clock-frequency = <12000000>; +}; + +&rtc_dev { + system-power-controller; +}; + +&udc { + phys = <&usb_phy>; +}; + +&pinctrl { + pins_nemc: nemc { + function = "nand"; + groups = "nand-cs1", "nand-cle-ale", "nand-fre-fwe"; + }; + + pins_pwm3: pwm3 { + function = "pwm3"; + groups = "pwm3"; + bias-disable; + }; + + pins_lcd: lcd { + function = "lcd"; + }; + + pins_mmc1: mmc1 { + mmc1-func { + function = "mmc1"; + groups = "mmc1-1bit"; + }; + + pins_mmc1_cd: mmc1-cd { + bias-pull-up; + }; + }; +}; + +&mmc0 { + status = "disabled"; +}; + +&mmc1 { + bus-width = <1>; + max-frequency = <50000000>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc1>; +}; + +&uart { + /* + * The pins for RX/TX are used for the right shoulder button and + * backlight PWM. + */ + status = "disabled"; +}; + +&nemc { + nandc: nand-controller@1 { + compatible = "ingenic,jz4725b-nand"; + reg = <1 0 0x4000000>; + + #address-cells = <1>; + #size-cells = <0>; + + ecc-engine = <&bch>; + + ingenic,nemc-tAS = <10>; + ingenic,nemc-tAH = <5>; + ingenic,nemc-tBP = <10>; + ingenic,nemc-tAW = <15>; + ingenic,nemc-tSTRV = <100>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_nemc>; + + rb-gpios = <&gpc 27 GPIO_ACTIVE_HIGH>; + + nand@1 { + reg = <1>; + + nand-ecc-step-size = <512>; + nand-ecc-strength = <8>; + nand-ecc-mode = "hw"; + nand-is-boot-medium; + nand-on-flash-bbt; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x20000>; + }; + + partition@20000 { + label = "system"; + reg = <0x20000 0x0>; + }; + }; + }; + }; +}; + +&cgu { + /* Use 32kHz oscillator as the parent of the RTC clock */ + assigned-clocks = <&cgu JZ4725B_CLK_RTC>; + assigned-clock-parents = <&cgu JZ4725B_CLK_OSC32K>; +}; + +&tcu { + /* + * 750 kHz for the system timer and clocksource, and use RTC as the + * parent for the watchdog clock. + */ + assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>, <&tcu TCU_CLK_WDT>; + assigned-clock-parents = <0>, <0>, <&cgu JZ4725B_CLK_RTC>; + assigned-clock-rates = <750000>, <750000>; +}; + +&lcd { + memory-region = <&vmem>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; +}; + +&lcd_ports { + port@0 { + reg = <0>; + + panel_output: endpoint { + remote-endpoint = <&panel_input>; + }; + }; +}; + +&cpu0 { + operating-points-v2 = <&cpu_opp_table>; + + /* We use the main PLL as the CPU clock for the cpufreq driver. */ + clocks = <&cgu JZ4725B_CLK_PLL>; +}; diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index 37c46720c719a6..2b959beb9527ef 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -67,14 +67,14 @@ }; }; - eth0_power: fixedregulator@0 { + eth0_power: fixedregulator-0 { compatible = "regulator-fixed"; regulator-name = "eth0_power"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - gpio = <&gpb 25 GPIO_ACTIVE_LOW>; + gpio = <&gpb 25 0>; enable-active-high; }; @@ -97,25 +97,63 @@ gpios = <&gpe 3 GPIO_ACTIVE_LOW>; }; - wlan0_power: fixedregulator@1 { + bt_power: fixedregulator-1 { compatible = "regulator-fixed"; - regulator-name = "wlan0_power"; + regulator-name = "bt_power"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-settling-time-us = <1400>; + + vin-supply = <&vcc_50>; - gpio = <&gpb 19 GPIO_ACTIVE_LOW>; + gpio = <&gpb 19 0>; enable-active-high; }; - otg_power: fixedregulator@2 { + otg_power: fixedregulator-2 { compatible = "regulator-fixed"; regulator-name = "otg_power"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; - gpio = <&gpf 14 GPIO_ACTIVE_LOW>; + gpio = <&gpf 14 0>; enable-active-high; }; + + wifi_power: fixedregulator-4 { + compatible = "regulator-fixed"; + + regulator-name = "wifi_power"; + + /* + * Technically it's 5V, the WiFi chip has its own internal + * regulators; but the MMC/SD subsystem won't accept such a + * value. */ + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-settling-time-us = <150000>; + + vin-supply = <&bt_power>; + }; + + vcc_33v: fixedregulator-5 { + compatible = "regulator-fixed"; + + regulator-name = "vcc_33v"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + wifi_pwrseq: pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpf 7 GPIO_ACTIVE_LOW>; + + clocks = <&rtc_dev>; + clock-names = "ext_clock"; + }; }; &ext { @@ -129,10 +167,11 @@ */ assigned-clocks = <&cgu JZ4780_CLK_OTGPHY>, <&cgu JZ4780_CLK_RTC>, <&cgu JZ4780_CLK_SSIPLL>, <&cgu JZ4780_CLK_SSI>, - <&cgu JZ4780_CLK_HDMI>; + <&cgu JZ4780_CLK_HDMI>, <&cgu JZ4780_CLK_MSCMUX>; assigned-clock-parents = <0>, <&cgu JZ4780_CLK_RTCLK>, <&cgu JZ4780_CLK_MPLL>, - <&cgu JZ4780_CLK_SSIPLL>; + <&cgu JZ4780_CLK_SSIPLL>, + <0>, <&cgu JZ4780_CLK_MPLL>; assigned-clock-rates = <48000000>, <0>, <54000000>, <0>, <27000000>; }; @@ -160,6 +199,8 @@ pinctrl-0 = <&pins_mmc0>; cd-gpios = <&gpf 20 GPIO_ACTIVE_LOW>; + vmmc-supply = <&vcc_33v>; + vqmmc-supply = <&vcc_33v>; }; &mmc1 { @@ -167,17 +208,26 @@ bus-width = <4>; max-frequency = <50000000>; + mmc-pwrseq = <&wifi_pwrseq>; + vmmc-supply = <&wifi_power>; + vqmmc-supply = <&wifi_io>; non-removable; pinctrl-names = "default"; pinctrl-0 = <&pins_mmc1>; - brcmf: wifi@1 { -/* reg = <4>;*/ - compatible = "brcm,bcm4330-fmac"; - vcc-supply = <&wlan0_power>; - device-wakeup-gpios = <&gpd 9 GPIO_ACTIVE_HIGH>; - shutdown-gpios = <&gpf 7 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + + wifi@1 { + compatible = "ingenic,iw8103-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + + /* + interrupt-parent = <&gpd>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "host-wake"; + */ }; }; @@ -204,11 +254,20 @@ bluetooth { compatible = "brcm,bcm4330-bt"; - reset-gpios = <&gpf 8 GPIO_ACTIVE_HIGH>; - vcc-supply = <&wlan0_power>; + + vbat-supply = <&bt_power>; + vddio-supply = <&wifi_io>; + + interrupt-parent = <&gpf>; + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "host-wakeup"; + + clocks = <&rtc_dev>; + clock-names = "lpo"; + + reset-gpios = <&gpf 8 GPIO_ACTIVE_LOW>; device-wakeup-gpios = <&gpf 5 GPIO_ACTIVE_HIGH>; - host-wakeup-gpios = <&gpf 6 GPIO_ACTIVE_HIGH>; - shutdown-gpios = <&gpf 4 GPIO_ACTIVE_LOW>; + shutdown-gpios = <&gpf 4 GPIO_ACTIVE_HIGH>; }; }; @@ -237,59 +296,59 @@ act8600: act8600@5a { compatible = "active-semi,act8600"; reg = <0x5a>; - status = "okay"; regulators { - vddcore: SUDCDC1 { - regulator-name = "DCDC_REG1"; + vddcore: DCDC1 { regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1100000>; + vp1-supply = <&vcc_33v>; + regulator-always-on; }; - vddmem: SUDCDC2 { - regulator-name = "DCDC_REG2"; + vddmem: DCDC2 { regulator-min-microvolt = <1500000>; regulator-max-microvolt = <1500000>; + vp2-supply = <&vcc_33v>; + regulator-always-on; }; - vcc_33: SUDCDC3 { - regulator-name = "DCDC_REG3"; + vcc_33: DCDC3 { regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + vp3-supply = <&vcc_33v>; + regulator-always-on; }; - vcc_50: SUDCDC4 { - regulator-name = "SUDCDC_REG4"; + vcc_50: SUDCDC_REG4 { regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; + regulator-always-on; }; - vcc_25: LDO_REG5 { - regulator-name = "LDO_REG5"; + vcc_25: LDO5 { regulator-min-microvolt = <2500000>; regulator-max-microvolt = <2500000>; + inl-supply = <&vcc_33v>; + regulator-always-on; }; - wifi_io: LDO_REG6 { - regulator-name = "LDO_REG6"; - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <2500000>; - regulator-always-on; + wifi_io: LDO6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-settling-time-us = <150000>; + inl-supply = <&vcc_33v>; }; - vcc_28: LDO_REG7 { - regulator-name = "LDO_REG7"; + cim_io_28: LDO7 { regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; - regulator-always-on; + inl-supply = <&vcc_33v>; }; - vcc_15: LDO_REG8 { - regulator-name = "LDO_REG8"; + cim_io_15: LDO8 { regulator-min-microvolt = <1500000>; regulator-max-microvolt = <1500000>; - regulator-always-on; + inl-supply = <&vcc_33v>; }; vrtc_18: LDO_REG9 { - regulator-name = "LDO_REG9"; /* Despite the datasheet stating 3.3V * for REG9 and the driver expecting that, * REG9 outputs 1.8V. @@ -300,12 +359,13 @@ */ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + regulator-always-on; }; vcc_11: LDO_REG10 { - regulator-name = "LDO_REG10"; regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; + regulator-always-on; }; }; @@ -363,7 +423,7 @@ #address-cells = <1>; #size-cells = <0>; - ingenic,bch-controller = <&bch>; + ecc-engine = <&bch>; ingenic,nemc-tAS = <10>; ingenic,nemc-tAH = <5>; @@ -429,8 +489,8 @@ pinctrl-names = "default"; pinctrl-0 = <&pins_nemc_cs6>; - reg = <6 0 1 /* addr */ - 6 2 1>; /* data */ + reg = <6 0 1>, /* addr */ + <6 2 1>; /* data */ ingenic,nemc-tAS = <15>; ingenic,nemc-tAH = <10>; @@ -438,11 +498,11 @@ ingenic,nemc-tAW = <50>; ingenic,nemc-tSTRV = <100>; - reset-gpios = <&gpf 12 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpf 12 GPIO_ACTIVE_LOW>; vcc-supply = <ð0_power>; interrupt-parent = <&gpe>; - interrupts = <19 4>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; nvmem-cells = <ð0_addr>; nvmem-cell-names = "mac-address"; diff --git a/arch/mips/boot/dts/ingenic/gcw0.dts b/arch/mips/boot/dts/ingenic/gcw0.dts index 5d33f26fd28c8b..1ead60380f697f 100644 --- a/arch/mips/boot/dts/ingenic/gcw0.dts +++ b/arch/mips/boot/dts/ingenic/gcw0.dts @@ -2,110 +2,23 @@ /dts-v1/; #include "jz4770.dtsi" -#include - -#include -#include -#include +#include "bits/gcw0-base.dtsi" / { compatible = "gcw,zero", "ingenic,jz4770"; model = "GCW Zero"; - aliases { - serial0 = &uart0; - serial1 = &uart1; - serial2 = &uart2; - serial3 = &uart3; - }; - - memory: memory { - device_type = "memory"; - reg = <0x0 0x10000000>, - <0x30000000 0x10000000>; - }; - - chosen { - stdout-path = "serial2:57600n8"; - }; - - vcc: regulator@0 { - compatible = "regulator-fixed"; - regulator-name = "vcc"; - - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - mmc1_power: regulator@1 { + usb_vbus: regulator-2 { compatible = "regulator-fixed"; - regulator-name = "mmc1_vcc"; - gpio = <&gpe 9 0>; + regulator-name = "USB Power"; + gpio = <&gpf 10 0>; + regulator-always-on; /* always ON for now */ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; vin-supply = <&vcc>; }; - headphones_amp: analog-amplifier@0 { - compatible = "simple-audio-amplifier"; - enable-gpios = <&gpf 3 GPIO_ACTIVE_LOW>; - enable-delay-ms = <50>; - - VCC-supply = <&ldo5>; - sound-name-prefix = "Headphones Amp"; - }; - - speaker_amp: analog-amplifier@1 { - compatible = "simple-audio-amplifier"; - enable-gpios = <&gpf 20 GPIO_ACTIVE_HIGH>; - - VCC-supply = <&ldo5>; - sound-name-prefix = "Speaker Amp"; - }; - - sound { - compatible = "simple-audio-card"; - - simple-audio-card,name = "gcw0-audio"; - simple-audio-card,format = "i2s"; - - simple-audio-card,widgets = - "Speaker", "Speaker", - "Headphone", "Headphones", - "Microphone", "Built-in Mic"; - simple-audio-card,routing = - "Headphones Amp INL", "LHPOUT", - "Headphones Amp INR", "RHPOUT", - "Headphones", "Headphones Amp OUTL", - "Headphones", "Headphones Amp OUTR", - "Speaker Amp INL", "LOUT", - "Speaker Amp INR", "ROUT", - "Speaker", "Speaker Amp OUTL", - "Speaker", "Speaker Amp OUTR", - "LLINEIN", "Cap-less", - "RLINEIN", "Cap-less", - "Built-in Mic", "MICBIAS", - "MIC1P", "Built-in Mic", - "MIC1N", "Built-in Mic"; - simple-audio-card,pin-switches = "Speaker", "Headphones"; - - simple-audio-card,hp-det-gpio = <&gpf 21 GPIO_ACTIVE_LOW>; - simple-audio-card,aux-devs = <&speaker_amp>, <&headphones_amp>; - - simple-audio-card,bitclock-master = <&dai_codec>; - simple-audio-card,frame-master = <&dai_codec>; - - dai_cpu: simple-audio-card,cpu { - sound-dai = <&aic>; - }; - - dai_codec: simple-audio-card,codec { - sound-dai = <&codec>; - }; - }; - rumble { compatible = "pwm-vibrator"; pwms = <&pwm 4 2000000 0>; @@ -115,127 +28,6 @@ pinctrl-0 = <&pins_pwm4>; }; - backlight: backlight { - compatible = "pwm-backlight"; - pwms = <&pwm 1 40000 0>; - power-supply = <&vcc>; - - brightness-levels = <0 16 32 48 64 80 96 112 128 - 144 160 176 192 208 224 240 255>; - default-brightness-level = <12>; - - pinctrl-names = "default"; - pinctrl-0 = <&pins_pwm1>; - }; - - gpio-keys { - compatible = "gpio-keys"; - autorepeat; - - button-0 { - label = "D-pad up"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 21 GPIO_ACTIVE_LOW>; - }; - - button-1 { - label = "D-pad down"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 25 GPIO_ACTIVE_LOW>; - }; - - button-2 { - label = "D-pad left"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 23 GPIO_ACTIVE_LOW>; - }; - - button-3 { - label = "D-pad right"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 24 GPIO_ACTIVE_LOW>; - }; - - button-4 { - label = "Button A"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 29 GPIO_ACTIVE_LOW>; - }; - - button-5 { - label = "Button B"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 20 GPIO_ACTIVE_LOW>; - }; - - button-6 { - label = "Button Y"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 27 GPIO_ACTIVE_LOW>; - }; - - button-7 { - label = "Button X"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 28 GPIO_ACTIVE_LOW>; - }; - - button-8 { - label = "Left shoulder button"; - linux,code = ; - linux,can-disable; - gpios = <&gpb 20 GPIO_ACTIVE_LOW>; - }; - - button-9 { - label = "Right shoulder button"; - linux,code = ; - linux,can-disable; - gpios = <&gpe 26 GPIO_ACTIVE_LOW>; - }; - - button-10 { - label = "Start button"; - linux,code = ; - linux,can-disable; - gpios = <&gpb 21 GPIO_ACTIVE_LOW>; - }; - - button-11 { - label = "Select button"; - linux,code = ; - linux,can-disable; - /* - * This is the only button that is active high, - * since it doubles as BOOT_SEL1. - */ - gpios = <&gpd 18 GPIO_ACTIVE_HIGH>; - }; - - button-12 { - label = "Power slider"; - linux,code = ; - linux,can-disable; - gpios = <&gpa 30 GPIO_ACTIVE_LOW>; - wakeup-source; - }; - - button-13 { - label = "Power hold"; - linux,code = ; - linux,can-disable; - gpios = <&gpf 11 GPIO_ACTIVE_LOW>; - }; - }; - i2c3: i2c-controller@3 { compatible = "i2c-gpio"; #address-cells = <1>; @@ -280,11 +72,10 @@ inl-supply = <&vcc>; }; - /* ??? */ - LDO7 { + /* HDMI chip */ + ldo7: LDO7 { regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; - /*regulator-always-on;*/ inl-supply = <&vcc>; }; @@ -316,156 +107,190 @@ }; }; - leds { - compatible = "gpio-leds"; - - led { - gpios = <&gpb 30 GPIO_ACTIVE_LOW>; - default-state = "on"; - }; - }; - - spi { - compatible = "spi-gpio"; + i2c4: i2c-controller@4 { + compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; - sck-gpios = <&gpe 15 GPIO_ACTIVE_HIGH>; - mosi-gpios = <&gpe 17 GPIO_ACTIVE_HIGH>; - cs-gpios = <&gpe 16 GPIO_ACTIVE_HIGH>; - num-chipselects = <1>; + sda-gpios = <&gpd 6 GPIO_ACTIVE_HIGH>; + scl-gpios = <&gpd 7 GPIO_ACTIVE_HIGH>; + clock-frequency = <100000>; - nt39016@0 { - compatible = "kingdisplay,kd035g6-54nt"; - reg = <0>; + it6610: hdmi@4c { + status = "disabled"; /* Disable until the driver works */ + + compatible = "ite,it6610"; + reg = <0x4c>; + + interrupt-parent = <&gpf>; + interrupts = <12 0>; + interrupt-names = "irq"; + + reset-gpios = <&gpe 6 GPIO_ACTIVE_LOW>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_it6610>; - spi-max-frequency = <3125000>; - spi-3wire; + power-supply = <&ldo7>; - reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + ports { + #address-cells = <1>; + #size-cells = <0>; - backlight = <&backlight>; - power-supply = <&ldo6>; + port@0 { + reg = <0>; - port { - panel_input: endpoint { - remote-endpoint = <&panel_output>; + hdmi_input: endpoint { + bus-width = <24>; + remote-endpoint = <&hdmi_output>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_connector_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; }; }; }; }; - connector { - compatible = "gpio-usb-b-connector", "usb-b-connector"; - label = "mini-USB"; - type = "mini"; + dc_charger: dc-charger { + compatible = "gpio-charger"; + charger-type = "mains"; + gpios = <&gpf 5 GPIO_ACTIVE_HIGH>; - /* - * USB OTG is not yet working reliably, the ID detection - * mechanism tends to fry easily for unknown reasons. - * Until this is fixed, disable OTG by not providing the - * ID GPIO to the driver. - */ - //id-gpios = <&gpf 18 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pins_dc_charger>; + }; - vbus-gpios = <&gpb 5 GPIO_ACTIVE_HIGH>; - vbus-supply = <&otg_vbus>; + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; - pinctrl-names = "default"; - pinctrl-0 = <&pins_otg>; + type = "c"; port { - usb_ep: endpoint { - remote-endpoint = <&usb_otg_ep>; + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_connector_out>; }; }; }; }; -&ext { - clock-frequency = <12000000>; +&headphones_amp { + VCC-supply = <&ldo5>; }; -&pinctrl { - pins_lcd: lcd { - function = "lcd"; - groups = "lcd-24bit"; - }; +&speaker_amp { + VCC-supply = <&ldo5>; +}; - pins_uart2: uart2 { - function = "uart2"; - groups = "uart2-data"; - }; +&sound_card { + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones", + "Microphone", "Built-in Mic"; + simple-audio-card,routing = + "Headphones Amp INL", "LHPOUT", + "Headphones Amp INR", "RHPOUT", + "Headphones", "Headphones Amp OUTL", + "Headphones", "Headphones Amp OUTR", + "Speaker Amp INL", "LOUT", + "Speaker Amp INR", "ROUT", + "Speaker", "Speaker Amp OUTL", + "Speaker", "Speaker Amp OUTR", + "LLINEIN", "Cap-less", + "RLINEIN", "Cap-less", + "Built-in Mic", "MICBIAS", + "MIC1P", "Built-in Mic", + "MIC1N", "Built-in Mic"; + simple-audio-card,pin-switches = "Speaker", "Headphones"; +}; - pins_mmc0: mmc0 { - function = "mmc0"; - groups = "mmc0-1bit-a", "mmc0-4bit-a"; - }; +&btn6 { + label = "Button Y"; +}; - pins_mmc1: mmc1 { - function = "mmc1"; - groups = "mmc1-1bit-d", "mmc1-4bit-d"; +&btn7 { + label = "Button X"; +}; + +&gpio_keys { + btn13: button-13 { + label = "Power hold"; + linux,code = ; + linux,can-disable; + gpios = <&gpf 11 GPIO_ACTIVE_LOW>; }; +}; - pins_otg: otg { - otg-vbus-pin { - function = "otg"; - groups = "otg-vbus"; - }; +&spi0_gpio { + status = "okay"; + + panel: nt39016@0 { + compatible = "kingdisplay,kd035g6-54nt"; + reg = <0>; + + spi-max-frequency = <3125000>; + spi-3wire; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&ldo6>; - vbus-pin { - pins = "PB5"; - bias-disable; + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; }; }; +}; + +&usb_conn { + vbus-supply = <&otg_vbus>; +}; + +&pmu { + power-supplies = <&act8600>, <&usb_conn>, <&dc_charger>; +}; + +&js_axis0 { + abs-range = <3300 0>; +}; + +&js_axis1 { + abs-range = <0 3300>; +}; + +&pinctrl { + pins_i2c0: i2c0 { + function = "i2c0"; + groups = "i2c0-data"; + }; - pins_pwm1: pwm1 { - function = "pwm1"; - groups = "pwm1"; + pins_i2c1: i2c1 { + function = "i2c1"; + groups = "i2c1-data"; + }; + + pins_it6610: it6610 { + pins = "PF12"; + bias-pull-up; }; pins_pwm4: pwm4 { function = "pwm4"; groups = "pwm4"; }; -}; -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pins_uart2>; - - status = "okay"; -}; - -&cgu { - /* - * Put high-speed peripherals under PLL1, such that we can change the - * PLL0 frequency on demand without having to suspend peripherals. - * We use a rate of 432 MHz, which is the least common multiple of - * 27 MHz (required by TV encoder) and 48 MHz (required by USB host). - * Put the GPU under PLL0 since we want a higher frequency. - * Use the 32 kHz oscillator as the parent of the RTC for a higher - * precision. - */ - assigned-clocks = - <&cgu JZ4770_CLK_PLL1>, - <&cgu JZ4770_CLK_GPU>, - <&cgu JZ4770_CLK_RTC>, - <&cgu JZ4770_CLK_UHC>, - <&cgu JZ4770_CLK_LPCLK_MUX>, - <&cgu JZ4770_CLK_MMC0_MUX>, - <&cgu JZ4770_CLK_MMC1_MUX>; - assigned-clock-parents = - <0>, - <&cgu JZ4770_CLK_PLL0>, - <&cgu JZ4770_CLK_OSC32K>, - <&cgu JZ4770_CLK_PLL1>, - <&cgu JZ4770_CLK_PLL1>, - <&cgu JZ4770_CLK_PLL1>, - <&cgu JZ4770_CLK_PLL1>; - assigned-clock-rates = - <432000000>, - <600000000>; + pins_dc_charger: dc-charger { + pins = "PF5"; + bias-pull-down; + }; }; &uhc { @@ -473,70 +298,58 @@ status = "okay"; }; -&tcu { - /* - * 750 kHz for the system timer and clocksource, 12 MHz for the OST, - * and use RTC as the parent for the watchdog clock - */ - assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>, - <&tcu TCU_CLK_OST>, <&tcu TCU_CLK_WDT>; - assigned-clock-parents = <0>, <0>, <0>, <&cgu JZ4770_CLK_RTC>; - assigned-clock-rates = <750000>, <750000>, <12000000>; - - /* PWM1 is in use, so use channel #2 for the clocksource */ - ingenic,pwm-channels-mask = <0xfa>; -}; - -&usb_otg { - port { - usb_otg_ep: endpoint { - remote-endpoint = <&usb_ep>; - }; - }; -}; - &otg_phy { vcc-supply = <&ldo5>; }; -&rtc { - clocks = <&cgu JZ4770_CLK_RTC>; - clock-names = "rtc"; +&i2c0 { + status = "okay"; - system-power-controller; -}; + pinctrl-names = "default"; + pinctrl-0 = <&pins_i2c0>; -&mmc0 { - status = "okay"; + clock-frequency = <400000>; /* 400 kHz */ - bus-width = <4>; - max-frequency = <48000000>; - vmmc-supply = <&vcc>; - non-removable; + radio: radio@11 { + compatible = "rda,rda5807"; + reg = <0x11>; - pinctrl-names = "default"; - pinctrl-0 = <&pins_mmc0>; + rda,lnan; + rda,lnap; + rda,analog-out; + + power-supply = <&ldo6>; + }; }; -&mmc1 { +&i2c1 { status = "okay"; - bus-width = <4>; - max-frequency = <48000000>; - cd-gpios = <&gpb 2 GPIO_ACTIVE_LOW>; - vmmc-supply = <&mmc1_power>; - pinctrl-names = "default"; - pinctrl-0 = <&pins_mmc1>; + pinctrl-0 = <&pins_i2c1>; + + clock-frequency = <100000>; /* 100 kHz */ + + accelerometer@15 { + compatible = "memsic,mxc6225"; + reg = <0x15>; + }; }; -&lcd { - pinctrl-names = "default"; - pinctrl-0 = <&pins_lcd>; +&lcd_ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; - port { - panel_output: endpoint { + panel_output: endpoint@0 { + reg = <0>; remote-endpoint = <&panel_input>; }; + + hdmi_output: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmi_input>; + }; }; }; diff --git a/arch/mips/boot/dts/ingenic/gopher2.dts b/arch/mips/boot/dts/ingenic/gopher2.dts new file mode 100644 index 00000000000000..8191a4246bc556 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/gopher2.dts @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rs97.dts" + +/ { + compatible = "qishenglong,gopher2", "ingenic,jz4760"; + model = "Gopher 2"; + + lcd-panel-1 { + compatible = "qishenglong,gopher2b-lcd"; + + backlight = <&backlight>; + power-supply = <&lcd_power>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; + + port { + lcd_panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&memory { + reg = <0x0 0x4000000>; +}; + +&btn_up { + gpios = <&gpd 23 GPIO_ACTIVE_LOW>; +}; + +&btn_down { + gpios = <&gpd 22 GPIO_ACTIVE_LOW>; +}; + +&btn_left { + gpios = <&gpa 29 GPIO_ACTIVE_LOW>; +}; + +&btn_right { + gpios = <&gpd 24 GPIO_ACTIVE_LOW>; +}; + +&btn_a { + gpios = <&gpb 23 GPIO_ACTIVE_LOW>; +}; + +&btn_b { + gpios = <&gpb 24 GPIO_ACTIVE_LOW>; +}; + +&btn_y { + gpios = <&gpd 6 GPIO_ACTIVE_LOW>; +}; + +&btn_x { + gpios = <&gpd 7 GPIO_ACTIVE_LOW>; +}; + +&btn_ltrig { + gpios = <&gpd 12 GPIO_ACTIVE_LOW>; +}; + +&btn_rtrig { + gpios = <&gpd 5 GPIO_ACTIVE_LOW>; +}; + +&btn_power { + status = "disabled"; +}; + +&btn_star { + status = "disabled"; +}; + +&gpio_keys { + btn_c: button-14 { + label = "Button C"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 26 GPIO_ACTIVE_LOW>; + }; + + btn_z: button-15 { + label = "Button Z"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 0 GPIO_ACTIVE_LOW>; + }; +}; + +&audio_amp { + enable-gpios = <&gpa 28 GPIO_ACTIVE_HIGH>; +}; + +&sound_card { + simple-audio-card,hp-det-gpio = <&gpe 7 GPIO_ACTIVE_HIGH>; +}; + +&lcd_power { + gpio = <&gpe 4 0>; +}; + +&spi { + status = "disabled"; +}; + +&panel { + status = "disabled"; +}; + +&usb_conn { + vbus-gpios = <&gpa 6 GPIO_ACTIVE_HIGH>; +}; + +&vdiv { + output-ohms = <346000>; + full-ohms = <1346000>; +}; + +&pins_lcd { + groups = "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit", "lcd-generic"; +}; + +&vbus_pin { + pins = "PA6"; +}; + +&panel_output { + remote-endpoint = <&lcd_panel_input>; +}; + +&panel_input { + remote-endpoint = <&panel_input>; +}; diff --git a/arch/mips/boot/dts/ingenic/gopher2b.dts b/arch/mips/boot/dts/ingenic/gopher2b.dts new file mode 100644 index 00000000000000..1b049f8234be50 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/gopher2b.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "gopher2.dts" +#include "jz4760b.dtsi" + +/ { + compatible = "qishenglong,gopher2b", "ingenic,jz4760b"; + model = "Gopher 2B"; +}; diff --git a/arch/mips/boot/dts/ingenic/jz4760.dtsi b/arch/mips/boot/dts/ingenic/jz4760.dtsi new file mode 100644 index 00000000000000..d177619599048a --- /dev/null +++ b/arch/mips/boot/dts/ingenic/jz4760.dtsi @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef JZ4760_DTSI +#define JZ4760_DTSI +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ingenic,jz4760"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "ingenic,xburst-fpu1.0-mxu1.1"; + reg = <0>; + + clocks = <&cgu JZ4760_CLK_CCLK>; + clock-names = "cpu"; + }; + }; + + cpuintc: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + + intc: interrupt-controller@10001000 { + compatible = "ingenic,jz4760-intc"; + reg = <0x10001000 0x40>; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>; + }; + + ext: ext { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + osc32k: osc32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + + cgu: clock-controller@10000000 { + compatible = "ingenic,jz4760-cgu", "simple-mfd"; + reg = <0x10000000 0x100>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x10000000 0x100>; + + clocks = <&ext>, <&osc32k>; + clock-names = "ext", "osc32k"; + + #clock-cells = <1>; + + otg_phy: usb-phy@3c { + compatible = "ingenic,jz4760-phy"; + reg = <0x3c 0x10>; + + clocks = <&cgu JZ4760_CLK_OTG_PHY>; + + #phy-cells = <0>; + }; + }; + + tcu: timer@10002000 { + compatible = "ingenic,jz4760-tcu", "simple-mfd"; + reg = <0x10002000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x10002000 0x1000>; + + #clock-cells = <1>; + + clocks = <&cgu JZ4760_CLK_RTC>, + <&cgu JZ4760_CLK_EXT>, + <&cgu JZ4760_CLK_PCLK>; + clock-names = "rtc", "ext", "pclk"; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&intc>; + interrupts = <27>, <26>, <25>; + + watchdog: watchdog@0 { + compatible = "ingenic,jz4760-watchdog", + "ingenic,jz4740-watchdog"; + reg = <0x0 0xc>; + + clocks = <&tcu TCU_CLK_WDT>; + clock-names = "wdt"; + }; + + pwm: pwm@40 { + compatible = "ingenic,jz4760-pwm", "ingenic,jz4740-pwm"; + reg = <0x40 0x80>; + + #pwm-cells = <3>; + + clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>, + <&tcu TCU_CLK_TIMER2>, <&tcu TCU_CLK_TIMER3>, + <&tcu TCU_CLK_TIMER4>, <&tcu TCU_CLK_TIMER5>, + <&tcu TCU_CLK_TIMER6>, <&tcu TCU_CLK_TIMER7>; + clock-names = "timer0", "timer1", "timer2", "timer3", + "timer4", "timer5", "timer6", "timer7"; + }; + + ost: timer@e0 { + compatible = "ingenic,jz4760-ost", "ingenic,jz4725b-ost"; + reg = <0xe0 0x20>; + + clocks = <&tcu TCU_CLK_OST>; + clock-names = "ost"; + + interrupts = <15>; + }; + }; + + rtc: rtc@10003000 { + compatible = "ingenic,jz4760-rtc"; + reg = <0x10003000 0x40>; + + interrupt-parent = <&intc>; + interrupts = <32>; + }; + + pinctrl: pinctrl@10010000 { + compatible = "ingenic,jz4760-pinctrl"; + reg = <0x10010000 0x600>; + + #address-cells = <1>; + #size-cells = <0>; + + gpa: gpio@0 { + compatible = "ingenic,jz4760-gpio"; + reg = <0>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 0 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <17>; + }; + + gpb: gpio@1 { + compatible = "ingenic,jz4760-gpio"; + reg = <1>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 32 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <16>; + }; + + gpc: gpio@2 { + compatible = "ingenic,jz4760-gpio"; + reg = <2>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 64 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <15>; + }; + + gpd: gpio@3 { + compatible = "ingenic,jz4760-gpio"; + reg = <3>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 96 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <14>; + }; + + gpe: gpio@4 { + compatible = "ingenic,jz4760-gpio"; + reg = <4>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 128 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <13>; + }; + + gpf: gpio@5 { + compatible = "ingenic,jz4760-gpio"; + reg = <5>; + + gpio-controller; + gpio-ranges = <&pinctrl 0 160 32>; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <12>; + }; + }; + + aic: audio-controller@10020000 { + compatible = "ingenic,jz4760-i2s"; + reg = <0x10020000 0x94>; + + #sound-dai-cells = <0>; + + clocks = <&cgu JZ4760_CLK_AIC>, <&cgu JZ4760_CLK_I2S>, + <&cgu JZ4760_CLK_EXT>, <&cgu JZ4760_CLK_PLL0_HALF>; + clock-names = "aic", "i2s", "ext", "pll half"; + + interrupt-parent = <&intc>; + interrupts = <34>; + + dmas = <&dmac1 25 0>, <&dmac1 24 1>; + dma-names = "rx", "tx"; + }; + + codec: audio-codec@100200a0 { + compatible = "ingenic,jz4760-codec"; + reg = <0x100200a4 0x8>; + + #sound-dai-cells = <0>; + + clocks = <&cgu JZ4760_CLK_AIC>; + clock-names = "aic"; + }; + + mmc0: mmc@10021000 { + compatible = "ingenic,jz4760-mmc"; + reg = <0x10021000 0x1000>; + + clocks = <&cgu JZ4760_CLK_MMC0>; + clock-names = "mmc"; + + interrupt-parent = <&intc>; + interrupts = <37>; + + dmas = <&dmac0 26 27 0xffffffff>; + dma-names = "tx-rx"; + + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + + status = "disabled"; + }; + + mmc1: mmc@10022000 { + compatible = "ingenic,jz4760-mmc"; + reg = <0x10022000 0x1000>; + + clocks = <&cgu JZ4760_CLK_MMC1>; + clock-names = "mmc"; + + interrupt-parent = <&intc>; + interrupts = <36>; + + dmas = <&dmac0 30 31 0xffffffff>; + dma-names = "tx-rx"; + + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + + status = "disabled"; + }; + + mmc2: mmc@10023000 { + compatible = "ingenic,jz4760-mmc"; + reg = <0x10023000 0x1000>; + + clocks = <&cgu JZ4760_CLK_MMC2>; + clock-names = "mmc"; + + interrupt-parent = <&intc>; + interrupts = <35>; + + dmas = <&dmac0 36 37 0xffffffff>; + dma-names = "tx-rx"; + + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + + status = "disabled"; + }; + + uart0: serial@10030000 { + compatible = "ingenic,jz4760-uart"; + reg = <0x10030000 0x100>; + + clocks = <&ext>, <&cgu JZ4760_CLK_UART0>; + clock-names = "baud", "module"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + status = "disabled"; + }; + + uart1: serial@10031000 { + compatible = "ingenic,jz4760-uart"; + reg = <0x10031000 0x100>; + + clocks = <&ext>, <&cgu JZ4760_CLK_UART1>; + clock-names = "baud", "module"; + + interrupt-parent = <&intc>; + interrupts = <4>; + + status = "disabled"; + }; + + uart2: serial@10032000 { + compatible = "ingenic,jz4760-uart"; + reg = <0x10032000 0x100>; + + clocks = <&ext>, <&cgu JZ4760_CLK_UART2>; + clock-names = "baud", "module"; + + interrupt-parent = <&intc>; + interrupts = <3>; + + status = "disabled"; + }; + + uart3: serial@10033000 { + compatible = "ingenic,jz4760-uart"; + reg = <0x10033000 0x100>; + + clocks = <&ext>, <&cgu JZ4760_CLK_UART3>; + clock-names = "baud", "module"; + + interrupt-parent = <&intc>; + interrupts = <2>; + + status = "disabled"; + }; + + adc: adc@10070000 { + compatible = "ingenic,jz4760-adc"; + reg = <0x10070000 0x30>; + + #io-channel-cells = <1>; + + clocks = <&cgu JZ4760_CLK_ADC>; + clock-names = "adc"; + + interrupt-parent = <&intc>; + interrupts = <18>; + }; + + mdmac: dma-controller@13030000 { + compatible = "ingenic,jz4760-mdma"; + reg = <0x13030000 0xc0>, <0x13030300 0x14>; + + #dma-cells = <2>; + + clocks = <&cgu JZ4760_CLK_MDMA>; + interrupt-parent = <&intc>; + interrupts = <22>; + + status = "disabled"; + }; + + lcd: lcd-controller@13050000 { + compatible = "ingenic,jz4760-lcd"; + reg = <0x13050000 0x300>; + + interrupt-parent = <&intc>; + interrupts = <31>; + + clocks = <&cgu JZ4760_CLK_LPCLK>; + clock-names = "lcd_pclk"; + }; + + ipu: ipu@13080000 { + compatible = "ingenic,jz4760-ipu"; + reg = <0x13080000 0x800>; + + interrupt-parent = <&intc>; + interrupts = <29>; + + clocks = <&cgu JZ4760_CLK_IPU>; + clock-names = "ipu"; + }; + + dmac0: dma-controller@13420000 { + compatible = "ingenic,jz4760-dma"; + reg = <0x13420000 0xc0>, <0x13420300 0x14>; + + #dma-cells = <3>; + + clocks = <&cgu JZ4760_CLK_DMA>; + interrupt-parent = <&intc>; + interrupts = <24>; + }; + + dmac1: dma-controller@13420100 { + compatible = "ingenic,jz4760-dma"; + reg = <0x13420100 0xc0>, <0x13420400 0x14>; + + #dma-cells = <2>; + + /* AIC seem to be able to only use DMAC1 channels 0 and 1 */ + ingenic,reserved-channels = <0x3>; + + clocks = <&cgu JZ4760_CLK_DMA>; + interrupt-parent = <&intc>; + interrupts = <23>; + }; + + uhc: usb@13430000 { + compatible = "generic-ohci"; + reg = <0x13430000 0x1000>; + + clocks = <&cgu JZ4760_CLK_UHC>; + assigned-clocks = <&cgu JZ4760_CLK_UHC>; + assigned-clock-rates = <48000000>; + + interrupt-parent = <&intc>; + interrupts = <20>; + + status = "disabled"; + }; + + usb_otg: usb@13440000 { + compatible = "ingenic,jz4760-musb", "ingenic,jz4740-musb"; + reg = <0x13440000 0x10000>; + + clocks = <&cgu JZ4760_CLK_OTG>; + clock-names = "udc"; + + interrupt-parent = <&intc>; + interrupts = <21>; + interrupt-names = "mc"; + + phys = <&otg_phy>; + + usb-role-switch; + }; + + rom: rom@1fc00000 { + compatible = "mtd-rom"; + probe-type = "map_rom"; + reg = <0x1fc00000 0x2000>; + + bank-width = <4>; + device-width = <1>; + }; +}; +#endif /* JZ4760_DTSI */ diff --git a/arch/mips/boot/dts/ingenic/jz4760b.dtsi b/arch/mips/boot/dts/ingenic/jz4760b.dtsi new file mode 100644 index 00000000000000..74a39baa031c5c --- /dev/null +++ b/arch/mips/boot/dts/ingenic/jz4760b.dtsi @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "jz4760.dtsi" + +/ { + compatible = "ingenic,jz4760b"; +}; + +&lcd { + compatible = "ingenic,jz4760b-lcd"; +}; + +&ost { + compatible = "ingenic,jz4760b-ost"; +}; + +&pinctrl { + compatible = "ingenic,jz4760b-pinctrl"; +}; + +&mdmac { + compatible = "ingenic,jz4760b-mdma"; + reg = <0x13030000 0xc0>, <0x13030300 0x1c>; +}; + +&dmac0 { + compatible = "ingenic,jz4760b-dma"; + reg = <0x13420000 0xc0>, <0x13420300 0x1c>; +}; + +&dmac1 { + compatible = "ingenic,jz4760b-dma"; + reg = <0x13420100 0xc0>, <0x13420400 0x1c>; +}; diff --git a/arch/mips/boot/dts/ingenic/jz4770.dtsi b/arch/mips/boot/dts/ingenic/jz4770.dtsi index bda0a3a86ed5f3..e3470531c9cd8c 100644 --- a/arch/mips/boot/dts/ingenic/jz4770.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4770.dtsi @@ -29,7 +29,7 @@ }; intc: interrupt-controller@10001000 { - compatible = "ingenic,jz4770-intc"; + compatible = "ingenic,jz4770-intc", "ingenic,jz4760-intc"; reg = <0x10001000 0x40>; interrupt-controller; @@ -73,7 +73,7 @@ }; tcu: timer@10002000 { - compatible = "ingenic,jz4770-tcu", "simple-mfd"; + compatible = "ingenic,jz4770-tcu", "ingenic,jz4760-tcu", "simple-mfd"; reg = <0x10002000 0x1000>; #address-cells = <1>; #size-cells = <1>; @@ -116,7 +116,7 @@ }; ost: timer@e0 { - compatible = "ingenic,jz4770-ost"; + compatible = "ingenic,jz4770-ost", "ingenic,jz4760b-ost"; reg = <0xe0 0x20>; clocks = <&tcu TCU_CLK_OST>; @@ -134,7 +134,7 @@ interrupts = <32>; }; - pinctrl: pin-controller@10010000 { + pinctrl: pinctrl@10010000 { compatible = "ingenic,jz4770-pinctrl"; reg = <0x10010000 0x600>; @@ -269,8 +269,8 @@ interrupt-parent = <&intc>; interrupts = <37>; - dmas = <&dmac1 27 0xffffffff>, <&dmac1 26 0xffffffff>; - dma-names = "rx", "tx"; + dmas = <&dmac1 26 27 0xffffffff>; + dma-names = "tx-rx"; cap-sd-highspeed; cap-mmc-highspeed; @@ -289,8 +289,8 @@ interrupt-parent = <&intc>; interrupts = <36>; - dmas = <&dmac1 31 0xffffffff>, <&dmac1 30 0xffffffff>; - dma-names = "rx", "tx"; + dmas = <&dmac1 30 31 0xffffffff>; + dma-names = "tx-rx"; cap-sd-highspeed; cap-mmc-highspeed; @@ -309,8 +309,8 @@ interrupt-parent = <&intc>; interrupts = <35>; - dmas = <&dmac1 37 0xffffffff>, <&dmac1 36 0xffffffff>; - dma-names = "rx", "tx"; + dmas = <&dmac1 36 37 0xffffffff>; + dma-names = "tx-rx"; cap-sd-highspeed; cap-mmc-highspeed; @@ -320,7 +320,7 @@ }; uart0: serial@10030000 { - compatible = "ingenic,jz4770-uart"; + compatible = "ingenic,jz4770-uart", "ingenic,jz4760-uart"; reg = <0x10030000 0x100>; clocks = <&ext>, <&cgu JZ4770_CLK_UART0>; @@ -333,7 +333,7 @@ }; uart1: serial@10031000 { - compatible = "ingenic,jz4770-uart"; + compatible = "ingenic,jz4770-uart", "ingenic,jz4760-uart"; reg = <0x10031000 0x100>; clocks = <&ext>, <&cgu JZ4770_CLK_UART1>; @@ -346,7 +346,7 @@ }; uart2: serial@10032000 { - compatible = "ingenic,jz4770-uart"; + compatible = "ingenic,jz4770-uart", "ingenic,jz4760-uart"; reg = <0x10032000 0x100>; clocks = <&ext>, <&cgu JZ4770_CLK_UART2>; @@ -359,7 +359,7 @@ }; uart3: serial@10033000 { - compatible = "ingenic,jz4770-uart"; + compatible = "ingenic,jz4770-uart", "ingenic,jz4760-uart"; reg = <0x10033000 0x100>; clocks = <&ext>, <&cgu JZ4770_CLK_UART3>; @@ -371,6 +371,88 @@ status = "disabled"; }; + ssi0: spi@10043000 { + compatible = "ingenic,jz4770-spi", "ingenic,jz4750-spi"; + reg = <0x10043000 0x1c>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <8>; + + clocks = <&cgu JZ4770_CLK_SSI0>; + + dmas = <&dmac0 23 0xffffffff>, <&dmac0 22 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; + }; + + ssi1: spi@10044000 { + compatible = "ingenic,jz4770-spi", "ingenic,jz4750-spi"; + reg = <0x10044000 0x1c>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <7>; + + clocks = <&cgu JZ4770_CLK_SSI1>; + + dmas = <&dmac0 33 0xffffffff>, <&dmac0 32 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; + }; + + i2c0: i2c@10050000 { + compatible = "ingenic,jz4770-i2c"; + reg = <0x10050000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <1>; + clocks = <&cgu JZ4770_CLK_I2C0>; + + dmas = <&dmac1 40 41 0xffffffff>; + dma-names = "tx-rx"; + + status = "disabled"; + }; + + i2c1: i2c@10051000 { + compatible = "ingenic,jz4770-i2c"; + reg = <0x10051000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <0>; + clocks = <&cgu JZ4770_CLK_I2C1>; + + dmas = <&dmac1 42 43 0xffffffff>; + dma-names = "tx-rx"; + + status = "disabled"; + }; + + i2c2: i2c@10055000 { + compatible = "ingenic,jz4770-i2c"; + reg = <0x10055000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <22>; + clocks = <&cgu JZ4770_CLK_I2C2>; + + dmas = <&dmac1 62 63 0xffffffff>; + dma-names = "tx-rx"; + + status = "disabled"; + }; + adc: adc@10070000 { compatible = "ingenic,jz4770-adc"; reg = <0x10070000 0x30>; @@ -406,6 +488,63 @@ clocks = <&cgu JZ4770_CLK_LPCLK_MUX>; clock-names = "lcd_pclk"; + + lcd_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + port@8 { + reg = <8>; + + ipu_output: endpoint { + remote-endpoint = <&ipu_input>; + }; + }; + }; + }; + + ipu: ipu@13080000 { + compatible = "ingenic,jz4770-ipu", "ingenic,jz4760-ipu"; + reg = <0x13080000 0x800>; + + interrupt-parent = <&intc>; + interrupts = <29>; + + clocks = <&cgu JZ4770_CLK_IPU>; + clock-names = "ipu"; + + port { + ipu_input: endpoint { + remote-endpoint = <&ipu_output>; + }; + }; + }; + + vpu: video-decoder@132a0000 { + compatible = "ingenic,jz4770-vpu-rproc", "simple-mfd"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x132a0000 0x20>; + + reg = <0x132a0000 0x20>, /* AUX */ + <0x132b0000 0x4000>, /* TCSM0 */ + <0x132c0000 0xc000>, /* TCSM1 */ + <0x132f0000 0x7000>; /* SRAM */ + reg-names = "aux", "tcsm0", "tcsm1", "sram"; + + clocks = <&cgu JZ4770_CLK_AUX>, <&cgu JZ4770_CLK_VPU>; + clock-names = "aux", "vpu"; + + interrupt-parent = <&cpuintc>; + interrupts = <3>; + + hwlock: hwlock@4 { + compatible = "ingenic,jz4770-vpu-hwspinlock", + "ingenic,jz4755-vpu-hwspinlock"; + reg = <0x4 0xc>; + + #hwlock-cells = <0>; + }; }; dmac0: dma-controller@13420000 { @@ -423,7 +562,7 @@ compatible = "ingenic,jz4770-dma"; reg = <0x13420100 0xC0>, <0x13420400 0x20>; - #dma-cells = <2>; + #dma-cells = <3>; clocks = <&cgu JZ4770_CLK_DMA>; interrupt-parent = <&intc>; @@ -460,7 +599,20 @@ usb-role-switch; }; - rom: memory@1fc00000 { + bdmac: dma-controller@13450000 { + compatible = "ingenic,jz4770-bdma", "ingenic,jz4760b-bdma"; + reg = <0x13450000 0xc0>, <0x13450300 0x20>; + + #dma-cells = <2>; + + clocks = <&cgu JZ4770_CLK_BDMA>; + interrupt-parent = <&intc>; + interrupts = <10>; + + status = "disabled"; + }; + + rom: rom@1fc00000 { compatible = "mtd-rom"; probe-type = "map_rom"; reg = <0x1fc00000 0x2000>; diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index c182a656d63bc0..18affff85ce38d 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -155,6 +155,8 @@ clocks = <&cgu JZ4780_CLK_RTCLK>; clock-names = "rtc"; + + #clock-cells = <0>; }; pinctrl: pin-controller@10010000 { diff --git a/arch/mips/boot/dts/ingenic/ldkh.dts b/arch/mips/boot/dts/ingenic/ldkh.dts new file mode 100644 index 00000000000000..0b528b2e571095 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/ldkh.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +/* LDK (horizontal) is a RS-97 with a different panel */ +#include "rs97.dts" + +/ { + compatible = "wolsen,ldkh", "ingenic,jz4760"; + model = "Wolsen LDK (horizontal)"; +}; + +&panel { + compatible = "innolux,ej030na"; +}; diff --git a/arch/mips/boot/dts/ingenic/ldkv.dts b/arch/mips/boot/dts/ingenic/ldkv.dts new file mode 100644 index 00000000000000..237a2ea0d9fabb --- /dev/null +++ b/arch/mips/boot/dts/ingenic/ldkv.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +/* LDK (vertical) is a LDK (horizontal) with a JZ4760B SoC */ +#include "ldkh.dts" +#include "jz4760b.dtsi" + +/ { + compatible = "wolsen,ldkv", "ingenic,jz4760b"; + model = "Wolsen LDK (vertical)"; +}; diff --git a/arch/mips/boot/dts/ingenic/papk3plus.dts b/arch/mips/boot/dts/ingenic/papk3plus.dts new file mode 100644 index 00000000000000..2652874072e87b --- /dev/null +++ b/arch/mips/boot/dts/ingenic/papk3plus.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "papk3s.dts" +#include "jz4760b.dtsi" + +/ { + compatible = "ylm,papk3plus", "ingenic,jz4760b"; + model = "PAP-KIII+"; +}; diff --git a/arch/mips/boot/dts/ingenic/papk3s.dts b/arch/mips/boot/dts/ingenic/papk3s.dts new file mode 100644 index 00000000000000..f2f3aab92d0f46 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/papk3s.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rs97.dts" + +/ { + compatible = "ylm,papk3s", "ingenic,jz4760"; + model = "PAP-KIIIS"; + + lcd-panel-1 { + compatible = "qishenglong,gopher2b-lcd"; + + power-supply = <&vcc>; + backlight = <&backlight>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; + + port { + lcd_panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&gpio_keys { + button-13 { + label = "Volume+ button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 21 GPIO_ACTIVE_LOW>; + }; + + button-14 { + label = "Volume- button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 25 GPIO_ACTIVE_LOW>; + }; +}; + + + +&spi { + status = "disabled"; +}; + +&panel { + status = "disabled"; +}; + +&pins_lcd { + groups = "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit", "lcd-generic"; +}; + +&panel_output { + remote-endpoint = <&lcd_panel_input>; +}; + +&panel_input { + remote-endpoint = <&panel_input>; +}; diff --git a/arch/mips/boot/dts/ingenic/pmp5.dts b/arch/mips/boot/dts/ingenic/pmp5.dts new file mode 100644 index 00000000000000..7a6318799afd22 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/pmp5.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rs90.dts" + +/ { + compatible = "ylm,pmp5", "ingenic,jz4725b"; + model = "PMP5"; + + keys@0 { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + + key@0 { + label = "D-pad left"; + linux,code = ; + gpios = <&gpb 31 GPIO_ACTIVE_LOW>; + }; + + key@1 { + label = "D-pad right"; + linux,code = ; + gpios = <&gpd 21 GPIO_ACTIVE_LOW>; + }; + + key@2 { + label = "Button A"; + linux,code = ; + gpios = <&gpc 31 GPIO_ACTIVE_LOW>; + }; + + key@3 { + label = "Button B"; + linux,code = ; + gpios = <&gpc 30 GPIO_ACTIVE_LOW>; + }; + + key@4 { + label = "Right shoulder button"; + linux,code = ; + gpios = <&gpc 12 GPIO_ACTIVE_LOW>; + debounce-interval = <10>; + }; + + key@5 { + label = "Start button"; + linux,code = ; + gpios = <&gpd 17 GPIO_ACTIVE_LOW>; + }; + }; + + keys@1 { + compatible = "adc-keys"; + io-channels = <&adc INGENIC_ADC_AUX>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <2800000>; + poll-interval = <30>; + + key@0 { + label = "D-pad up"; + linux,code = ; + press-threshold-microvolt = <1500000>; // Up key pressed voltage: ~1370 mV + }; + + key@1 { + label = "Select button"; + linux,code = ; + press-threshold-microvolt = <1200000>; // Select key pressed voltage: ~1060mV + }; + + key@2 { + label = "Left shoulder button"; + linux,code = ; + press-threshold-microvolt = <800000>; // L key pressed voltage: ~680mV + }; + + key@3 { + label = "D-pad down"; + linux,code = ; + press-threshold-microvolt = <500000>; // Down key pressed voltage: ~170mV + }; + }; +}; + +&pins_lcd { + groups = "lcd-8bit", "lcd-16bit", "lcd-special"; +}; + +&pins_mmc1_cd { + pins = "PC20"; +}; + +&mmc1 { + cd-gpios = <&gpc 20 GPIO_ACTIVE_LOW>; +}; diff --git a/arch/mips/boot/dts/ingenic/pocketgo2.dts b/arch/mips/boot/dts/ingenic/pocketgo2.dts new file mode 100644 index 00000000000000..adf7f425e87f5b --- /dev/null +++ b/arch/mips/boot/dts/ingenic/pocketgo2.dts @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "bits/gcw0-base.dtsi" + +/ { + compatible = "wolsen,pocketgo2", "ingenic,jz4770"; + model = "Wolsen PocketGo2/PlayGo"; + + panel { + compatible = "frida,frd350h54004"; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&gpio_keys { + btn13: button-13 { + label = "L2 shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 31 GPIO_ACTIVE_LOW>; + }; + + btn14: button-14 { + label = "R2 shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpf 10 GPIO_ACTIVE_LOW>; + }; + + btn15: button-15 { + label = "Volume+ button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 19 GPIO_ACTIVE_LOW>; + }; + + btn16: button-16 { + label = "Volume- button"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 13 GPIO_ACTIVE_LOW>; + }; + + btn17: button-17 { + label = "Menu button"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 8 GPIO_ACTIVE_LOW>; + }; +}; + +&js_axis0 { + abs-range = <3300 0>; +}; + +&js_axis1 { + abs-range = <0 3300>; +}; + +&lcd_ports { + port@0 { + reg = <0>; + + panel_output: endpoint { + remote-endpoint = <&panel_input>; + }; + }; +}; + +&sound_card { + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + simple-audio-card,routing = + "Headphones Amp INL", "LHPOUT", + "Headphones Amp INR", "RHPOUT", + "Headphones", "Headphones Amp OUTL", + "Headphones", "Headphones Amp OUTR", + "Speaker Amp INL", "LOUT", + "Speaker Amp INR", "ROUT", + "Speaker", "Speaker Amp OUTL", + "Speaker", "Speaker Amp OUTR"; + simple-audio-card,pin-switches = "Speaker", "Headphones"; +}; diff --git a/arch/mips/boot/dts/ingenic/pocketgo2v2.dts b/arch/mips/boot/dts/ingenic/pocketgo2v2.dts new file mode 100644 index 00000000000000..c20466e60dcf33 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/pocketgo2v2.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "pocketgo2.dts" + +/ { + compatible = "wolsen,pocketgo2v2", "ingenic,jz4770"; + model = "Wolsen PocketGo2/PlayGo Rev. 2"; +}; + +&sound_card { + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + simple-audio-card,routing = + "Headphones Amp INL", "LHPOUT", + "Headphones Amp INR", "RHPOUT", + "Headphones", "Headphones Amp OUTL", + "Headphones", "Headphones Amp OUTR", + "Speaker Amp INL", "Headphones Amp OUTL", + "Speaker Amp INR", "Headphones Amp OUTR", + "Speaker", "Speaker Amp OUTL", + "Speaker", "Speaker Amp OUTR"; + simple-audio-card,pin-switches = "Speaker"; +}; + +&joystick { + io-channels = <&adc INGENIC_ADC_TOUCH_YP>, + <&adc INGENIC_ADC_TOUCH_XP>; +}; + +&js_axis0 { + abs-range = <2400 200>; +}; + +&js_axis1 { + abs-range = <2200 0>; +}; + +&btn8 { + gpios = <&gpb 10 GPIO_ACTIVE_LOW>; +}; + +&btn13 { + gpios = <&gpb 20 GPIO_ACTIVE_LOW>; +}; + +&btn14 { + gpios = <&gpb 11 GPIO_ACTIVE_LOW>; +}; + +&btn15 { + gpios = <&gpb 12 GPIO_ACTIVE_LOW>; +}; + +&btn16 { + gpios = <&gpb 13 GPIO_ACTIVE_LOW>; +}; diff --git a/arch/mips/boot/dts/ingenic/qi_lb60.dts b/arch/mips/boot/dts/ingenic/qi_lb60.dts index ba021897157235..24f987244a1226 100644 --- a/arch/mips/boot/dts/ingenic/qi_lb60.dts +++ b/arch/mips/boot/dts/ingenic/qi_lb60.dts @@ -27,7 +27,7 @@ stdout-path = &uart0; }; - vcc: regulator@0 { + vcc: regulator-0 { compatible = "regulator-fixed"; regulator-name = "vcc"; @@ -36,7 +36,7 @@ regulator-always-on; }; - mmc_power: regulator@1 { + mmc_power: regulator-1 { compatible = "regulator-fixed"; regulator-name = "mmc_vcc"; gpio = <&gpd 2 0>; @@ -45,7 +45,7 @@ regulator-max-microvolt = <3300000>; }; - amp_supply: regulator@2 { + amp_supply: regulator-2 { compatible = "regulator-fixed"; regulator-name = "amp_supply"; gpio = <&gpd 4 0>; diff --git a/arch/mips/boot/dts/ingenic/rg280m-v1.0.dts b/arch/mips/boot/dts/ingenic/rg280m-v1.0.dts new file mode 100644 index 00000000000000..9bfb5b5cfa1206 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg280m-v1.0.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rg280m-v1.1.dts" + +#include + +/ { + compatible = "ylm,rg280m-v1.0", "ingenic,jz4770"; + model = "Anbernic RG-280M v1.0"; + + hdmi_power: regulator-3 { + compatible = "regulator-fixed"; + regulator-name = "hdmi_pwr"; + gpio = <&gpf 22 0>; + enable-active-high; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc>; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + label = "hdmi"; + + type = "c"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_connector_out>; + }; + }; + }; +}; + +&pinctrl { + pins_it66121: it66121 { + pins = "PF12"; + bias-pull-up; + }; + + pins_i2c0: i2c0 { + function = "i2c0"; + groups = "i2c0-data"; + }; +}; + +&i2c0 { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_i2c0>; + + clock-frequency = <100000>; /* 100 kHz */ + + hdmi@4c { + compatible = "ite,it66121"; + reg = <0x4c>; + + interrupt-parent = <&gpf>; + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "irq"; + + reset-gpios = <&gpe 6 GPIO_ACTIVE_LOW>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_it66121>; + + vrf12-supply = <&hdmi_power>; + vcn33-supply = <&hdmi_power>; + vcn18-supply = <&hdmi_power>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_input: endpoint { + bus-width = <24>; + remote-endpoint = <&hdmi_output>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_connector_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&lcd_port0 { + hdmi_output: endpoint@1 { + reg = <1>; + remote-endpoint = <&hdmi_input>; + }; +}; + +&pins_lcd { + /* The LCD's interface is 8 bits, but we need the 24-bit interface for HDMI */ + groups = "lcd-24bit"; +}; diff --git a/arch/mips/boot/dts/ingenic/rg280m-v1.1.dts b/arch/mips/boot/dts/ingenic/rg280m-v1.1.dts new file mode 100644 index 00000000000000..8338d4c93bbf45 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg280m-v1.1.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rg280v.dts" + +/ { + compatible = "ylm,rg280m-v1.1", "ingenic,jz4770"; + model = "Anbernic RG-280M v1.1"; +}; + +&joystick { + status = "okay"; + + io-channels = <&adc INGENIC_ADC_TOUCH_YP>, + <&adc INGENIC_ADC_TOUCH_XP>; +}; + +&js_axis0 { + abs-range = <3000 200>; +}; + +&js_axis1 { + abs-range = <3000 200>; +}; + +&sound_card { + simple-audio-card,routing = + "Headphones Amp INL", "LHPOUT", + "Headphones Amp INR", "RHPOUT", + "Headphones", "Headphones Amp OUTL", + "Headphones", "Headphones Amp OUTR", + "Speaker Amp INL", "Headphones Amp OUTL", + "Speaker Amp INR", "Headphones Amp OUTR", + "Speaker", "Speaker Amp OUTL", + "Speaker", "Speaker Amp OUTR"; +}; + +&gpio_keys { + btn17: button-17 { + label = "L3 joystick button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 14 GPIO_ACTIVE_LOW>; + }; +}; + +&uhc { + status = "okay"; +}; diff --git a/arch/mips/boot/dts/ingenic/rg280v.dts b/arch/mips/boot/dts/ingenic/rg280v.dts new file mode 100644 index 00000000000000..6d6d0b59da782a --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg280v.dts @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "bits/gcw0-base.dtsi" + +/ { + compatible = "ylm,rg280v", "ingenic,jz4770"; + model = "Anbernic RG-280V"; + + rumble: rumble { + compatible = "pwm-vibrator"; + pwms = <&pwm 4 2000000 0>; + pwm-names = "enable"; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_pwm4>; + }; +}; + +&joystick { + status = "disabled"; +}; + +&btn8 { + gpios = <&gpb 10 GPIO_ACTIVE_LOW>; +}; + +&gpio_keys { + btn13: button-13 { + label = "L2 shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 20 GPIO_ACTIVE_LOW>; + }; + + btn14: button-14 { + label = "R2 shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 11 GPIO_ACTIVE_LOW>; + }; + + btn15: button-15 { + label = "Volume+ button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 12 GPIO_ACTIVE_LOW>; + }; + + btn16: button-16 { + label = "Volume- button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 13 GPIO_ACTIVE_LOW>; + }; +}; + +&sound_card { + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + simple-audio-card,routing = + "Headphones Amp INL", "LHPOUT", + "Headphones Amp INR", "RHPOUT", + "Headphones", "Headphones Amp OUTL", + "Headphones", "Headphones Amp OUTR", + "Speaker Amp INL", "LOUT", + "Speaker Amp INR", "ROUT", + "Speaker", "Speaker Amp OUTL", + "Speaker", "Speaker Amp OUTR"; + simple-audio-card,pin-switches = "Speaker"; +}; + +&pinctrl { + pins_pwm4: pwm4 { + function = "pwm4"; + groups = "pwm4"; + }; +}; + +&spi0_gpio { + status = "okay"; + + panel: panel@0 { + compatible = "abt,y030xx067a"; + reg = <0>; + + spi-max-frequency = <3125000>; + spi-3wire; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&pins_lcd { + groups = "lcd-8bit"; +}; + +&lcd_ports { + lcd_port0: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + panel_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_input>; + }; + }; +}; diff --git a/arch/mips/boot/dts/ingenic/rg300.dts b/arch/mips/boot/dts/ingenic/rg300.dts new file mode 100644 index 00000000000000..c7fcfe8923ab16 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg300.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +/* RG-300 is a RS-97 with a JZ4760B and a different panel */ +#include "rs97.dts" +#include "jz4760b.dtsi" + +/ { + compatible = "ylm,rg300", "ingenic,jz4760b"; + model = "RG-300"; +}; + +&panel { + compatible = "abt,y030xx067a"; +}; diff --git a/arch/mips/boot/dts/ingenic/rg300x.dts b/arch/mips/boot/dts/ingenic/rg300x.dts new file mode 100644 index 00000000000000..7f4102c8bdbef4 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg300x.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rg350m.dts" + +#include + +/ { + compatible = "ylm,rg300x", "ingenic,jz4770"; + model = "Anbernic RG-300X"; +}; + +&panel { + compatible = "focaltech,gpt3"; +}; + +&btn17 { + status = "disabled"; +}; + +&btn18 { + status = "disabled"; +}; + +&joystick { + status = "disabled"; +}; + +&pinctrl { + pins_power_led: power-led { + pin = "PB30"; + input-enable; + bias-pull-up; + }; +}; + +/* + * On the RG-300X, the LEDs are way too powerful. Work around this by setting + * the GPIO as input with the pull-up resitor enabled, which causes the voltage + * to be low enough that the LEDs are just slightly lit. + */ +&power_led { + status = "disabled"; +}; + +&leds { + pinctrl-names = "default"; + pinctrl-0 = <&pins_power_led>; +}; diff --git a/arch/mips/boot/dts/ingenic/rg350.dts b/arch/mips/boot/dts/ingenic/rg350.dts new file mode 100644 index 00000000000000..4e618960fe1b0e --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg350.dts @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rg350m.dts" + +/ { + compatible = "ylm,rg350", "ingenic,jz4770"; + model = "Anbernic RG-350"; + + panel { + compatible = "frida,frd350h54004"; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + frida_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&panel { + status = "disabled"; +}; + +&spi0_gpio { + status = "disabled"; +}; + +/* Make ABT panel port a dummy to avoid DTC complaints */ +&panel_input { + remote-endpoint = <&panel_input>; +}; + +&panel_output { + remote-endpoint = <&frida_input>; +}; diff --git a/arch/mips/boot/dts/ingenic/rg350m.dts b/arch/mips/boot/dts/ingenic/rg350m.dts new file mode 100644 index 00000000000000..9ec5df054d48fa --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg350m.dts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rg280m-v1.0.dts" + +/ { + compatible = "ylm,rg350m", "ingenic,jz4770"; + model = "Anbernic RG-350M"; +}; + +&panel { + compatible = "leadtek,ltk035c5444t"; +}; + +&gpio_keys { + btn18: button-18 { + label = "R3 joystick button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 15 GPIO_ACTIVE_LOW>; + }; +}; + +&joystick { + io-channels = <&adc INGENIC_ADC_TOUCH_YP>, + <&adc INGENIC_ADC_TOUCH_XP>, + <&adc INGENIC_ADC_TOUCH_YN>, + <&adc INGENIC_ADC_TOUCH_XN>; + + js_axis2: axis@2 { + reg = <2>; + linux,code = ; + abs-range = <200 3000>; + abs-fuzz = <4>; + abs-flat = <200>; + }; + + js_axis3: axis@3 { + reg = <3>; + linux,code = ; + abs-range = <200 3000>; + abs-fuzz = <4>; + abs-flat = <200>; + }; +}; diff --git a/arch/mips/boot/dts/ingenic/rg99.dts b/arch/mips/boot/dts/ingenic/rg99.dts new file mode 100644 index 00000000000000..d99ae48b0e16a1 --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rg99.dts @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "bits/rs90-base.dtsi" + +/ { + compatible = "ylm,rg99", "ingenic,jz4725b"; + model = "RG-99"; + + spi { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = <&gpd 23 GPIO_ACTIVE_HIGH>; + mosi-gpios = <&gpd 22 GPIO_ACTIVE_HIGH>; + cs-gpios = <&gpd 10 GPIO_ACTIVE_HIGH>; + num-chipselects = <1>; + + panel@0 { + compatible = "abt,y030xx067a"; + reg = <0>; + + spi-max-frequency = <10000000>; + spi-3wire; + + reset-gpios = <&gpd 25 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + + button-0 { + label = "D-pad up"; + linux,code = ; + gpios = <&gpd 8 GPIO_ACTIVE_LOW>; + }; + + button-1 { + label = "D-pad down"; + linux,code = ; + gpios = <&gpd 9 GPIO_ACTIVE_LOW>; + }; + + button-2 { + label = "D-pad left"; + linux,code = ; + gpios = <&gpd 11 GPIO_ACTIVE_LOW>; + }; + + button-3 { + label = "D-pad right"; + linux,code = ; + gpios = <&gpc 20 GPIO_ACTIVE_LOW>; + }; + + button-4 { + label = "Button A"; + linux,code = ; + gpios = <&gpd 24 GPIO_ACTIVE_LOW>; + }; + + button-5 { + label = "Button B"; + linux,code = ; + gpios = <&gpd 16 GPIO_ACTIVE_LOW>; + }; + + button-6 { + label = "Right shoulder button"; + linux,code = ; + gpios = <&gpd 13 GPIO_ACTIVE_LOW>; + debounce-interval = <10>; + }; + + button-7 { + label = "Start button"; + linux,code = ; + gpios = <&gpc 31 GPIO_ACTIVE_LOW>; + }; + + button-8 { + label = "Left shoulder button"; + linux,code = ; + gpios = <&gpd 14 GPIO_ACTIVE_LOW>; + debounce-interval = <10>; + }; + + button-9 { + label = "Button X"; + linux,code = ; + gpios = <&gpd 17 GPIO_ACTIVE_LOW>; + }; + + button-10 { + label = "Button Y"; + linux,code = ; + gpios = <&gpd 15 GPIO_ACTIVE_LOW>; + }; + + button-11 { + label = "Select button"; + linux,code = ; + gpios = <&gpc 30 GPIO_ACTIVE_LOW>; + }; + + button-12 { + label = "Menu1 button"; + linux,code = ; + gpios = <&gpd 12 GPIO_ACTIVE_LOW>; + debounce-interval = <10>; + }; + + button-13 { + label = "Menu2 button"; + linux,code = ; + gpios = <&gpb 31 GPIO_ACTIVE_LOW>; + debounce-interval = <10>; + }; + }; + + usb_conn: connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + label = "mini-USB"; + type = "mini"; + + vbus-gpios = <&gpc 10 GPIO_ACTIVE_HIGH>; + vbus-supply = <&vcc>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_vbus>; + + port { + usb_conn_ep: endpoint { + remote-endpoint = <&usb_ep>; + }; + }; + }; +}; + +&udc { + port { + usb_ep: endpoint { + remote-endpoint = <&usb_conn_ep>; + }; + }; +}; + +&sound_card { + /* TODO: What's the headphones detection GPIO? */ + simple-audio-card,hp-det-gpio; +}; + +&pmu { + power-supplies = <&usb_conn>; +}; + +&pinctrl { + pins_vbus: vbus { + pins = "PC10"; + bias-disable; + }; +}; + +&pins_lcd { + groups = "lcd-8bit"; +}; + +&pins_mmc1_cd { + pins = "PD21"; +}; + +&mmc1 { + cd-gpios = <&gpd 21 GPIO_ACTIVE_LOW>; +}; + +&vmem { + /* Reserve 2 MiB of VRAM */ + reg = <0x1e00000 0x200000>; +}; + +&battery { + voltage-min-design-microvolt = <3900000>; + voltage-max-design-microvolt = <4200000>; +}; + +&resistor { + output-ohms = <28000>; + full-ohms = <100000>; +}; diff --git a/arch/mips/boot/dts/ingenic/rs07.dts b/arch/mips/boot/dts/ingenic/rs07.dts new file mode 100644 index 00000000000000..d4f6f0c06414ee --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rs07.dts @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "rs97.dts" +#include "jz4760b.dtsi" + +/ { + compatible = "ylm,rs07", "ingenic,jz4760b"; + model = "RS07"; + + lcd-panel-1 { + compatible = "qishenglong,gopher2b-lcd"; + + power-supply = <&vcc>; + backlight = <&backlight>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; + + port { + lcd_panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; +}; + +&gpio_keys { + button-4 { + label = "Button A"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 23 GPIO_ACTIVE_LOW>; + }; + + button-5 { + label = "Button B"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 16 GPIO_ACTIVE_LOW>; + }; + + button-6 { + label = "Button Y"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 22 GPIO_ACTIVE_LOW>; + }; + + button-7 { + label = "Button X"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 15 GPIO_ACTIVE_LOW>; + }; + + button-12 { + status = "disabled"; + }; + + button-13 { + status = "disabled"; + }; +}; + +&usb_conn { + vbus-gpio = <&gpe 13 GPIO_ACTIVE_HIGH>; + vbus-supply = <&vcc>; +}; + +&pinctrl { + otg { + vbus-pin { + pins = "PE13"; + }; + }; +}; + +&spi { + status = "disabled"; +}; + +&panel { + status = "disabled"; +}; + +&pins_lcd { + groups = "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-24bit", "lcd-generic"; +}; + +&panel_output { + remote-endpoint = <&lcd_panel_input>; +}; + +&panel_input { + remote-endpoint = <&panel_input>; +}; diff --git a/arch/mips/boot/dts/ingenic/rs90.dts b/arch/mips/boot/dts/ingenic/rs90.dts index e8df70dd42bf8e..9aea3a632a19b6 100644 --- a/arch/mips/boot/dts/ingenic/rs90.dts +++ b/arch/mips/boot/dts/ingenic/rs90.dts @@ -1,55 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /dts-v1/; -#include "jz4725b.dtsi" - -#include -#include -#include +#include "bits/rs90-base.dtsi" / { compatible = "ylm,rs90", "ingenic,jz4725b"; model = "RS-90"; - memory { - device_type = "memory"; - reg = <0x0 0x2000000>; - }; - - reserved-memory { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - vmem: video-memory@1f00000 { - compatible = "shared-dma-pool"; - reg = <0x1f00000 0x100000>; - reusable; - }; - }; - - vcc: regulator { - compatible = "regulator-fixed"; - - regulator-name = "vcc"; - regulaor-min-microvolt = <3300000>; - regulaor-max-microvolt = <3300000>; - regulator-always-on; - }; - - backlight: backlight { - compatible = "pwm-backlight"; - pwms = <&pwm 3 40000 0>; - - brightness-levels = <0 16 32 48 64 80 112 144 192 255>; - default-brightness-level = <8>; - - pinctrl-names = "default"; - pinctrl-0 = <&pins_pwm3>; - - power-supply = <&vcc>; - }; - keys@0 { compatible = "gpio-keys"; @@ -123,56 +80,6 @@ }; }; - amp: analog-amplifier { - compatible = "simple-audio-amplifier"; - enable-gpios = <&gpc 15 GPIO_ACTIVE_HIGH>; - - VCC-supply = <&vcc>; - }; - - sound { - compatible = "simple-audio-card"; - - simple-audio-card,name = "rs90-audio"; - simple-audio-card,format = "i2s"; - - simple-audio-card,widgets = - "Speaker", "Speaker", - "Headphone", "Headphones"; - simple-audio-card,routing = - "INL", "LHPOUT", - "INR", "RHPOUT", - "Headphones", "LHPOUT", - "Headphones", "RHPOUT", - "Speaker", "OUTL", - "Speaker", "OUTR"; - simple-audio-card,pin-switches = "Speaker"; - - simple-audio-card,hp-det-gpio = <&gpd 16 GPIO_ACTIVE_LOW>; - simple-audio-card,aux-devs = <&>; - - simple-audio-card,bitclock-master = <&dai_codec>; - simple-audio-card,frame-master = <&dai_codec>; - - dai_cpu: simple-audio-card,cpu { - sound-dai = <&aic>; - }; - - dai_codec: simple-audio-card,codec { - sound-dai = <&codec>; - }; - - }; - - usb_phy: usb-phy { - compatible = "usb-nop-xceiv"; - #phy-cells = <0>; - - clocks = <&cgu JZ4725B_CLK_UDC_PHY>; - clock-names = "main_clk"; - vcc-supply = <&vcc>; - }; - panel { compatible = "sharp,ls020b1dd01d"; @@ -187,141 +94,14 @@ }; }; -&ext { - clock-frequency = <12000000>; -}; - -&rtc_dev { - system-power-controller; +&pins_lcd { + groups = "lcd-8bit", "lcd-16bit", "lcd-special"; }; -&udc { - phys = <&usb_phy>; -}; - -&pinctrl { - pins_mmc1: mmc1 { - function = "mmc1"; - groups = "mmc1-1bit"; - }; - - pins_nemc: nemc { - function = "nand"; - groups = "nand-cs1", "nand-cle-ale", "nand-fre-fwe"; - }; - - pins_pwm3: pwm3 { - function = "pwm3"; - groups = "pwm3"; - bias-disable; - }; - - pins_lcd: lcd { - function = "lcd"; - groups = "lcd-8bit", "lcd-16bit", "lcd-special"; - }; -}; - -&mmc0 { - status = "disabled"; +&pins_mmc1_cd { + pins = "PC20"; }; &mmc1 { - bus-width = <1>; - max-frequency = <48000000>; - - pinctrl-names = "default"; - pinctrl-0 = <&pins_mmc1>; - cd-gpios = <&gpc 20 GPIO_ACTIVE_LOW>; }; - -&uart { - /* - * The pins for RX/TX are used for the right shoulder button and - * backlight PWM. - */ - status = "disabled"; -}; - -&nemc { - nandc: nand-controller@1 { - compatible = "ingenic,jz4725b-nand"; - reg = <1 0 0x4000000>; - - #address-cells = <1>; - #size-cells = <0>; - - ecc-engine = <&bch>; - - ingenic,nemc-tAS = <10>; - ingenic,nemc-tAH = <5>; - ingenic,nemc-tBP = <10>; - ingenic,nemc-tAW = <15>; - ingenic,nemc-tSTRV = <100>; - - pinctrl-names = "default"; - pinctrl-0 = <&pins_nemc>; - - rb-gpios = <&gpc 27 GPIO_ACTIVE_HIGH>; - - nand@1 { - reg = <1>; - - nand-ecc-step-size = <512>; - nand-ecc-strength = <8>; - nand-ecc-mode = "hw"; - nand-is-boot-medium; - nand-on-flash-bbt; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "bootloader"; - reg = <0x0 0x20000>; - }; - - partition@20000 { - label = "system"; - reg = <0x20000 0x0>; - }; - }; - }; - }; -}; - -&cgu { - /* Use 32kHz oscillator as the parent of the RTC clock */ - assigned-clocks = <&cgu JZ4725B_CLK_RTC>; - assigned-clock-parents = <&cgu JZ4725B_CLK_OSC32K>; -}; - -&tcu { - /* - * 750 kHz for the system timer and clocksource, and use RTC as the - * parent for the watchdog clock. - */ - assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER1>, <&tcu TCU_CLK_WDT>; - assigned-clock-parents = <0>, <0>, <&cgu JZ4725B_CLK_RTC>; - assigned-clock-rates = <750000>, <750000>; -}; - -&lcd { - memory-region = <&vmem>; - - pinctrl-names = "default"; - pinctrl-0 = <&pins_lcd>; -}; - -&lcd_ports { - port@0 { - reg = <0>; - - panel_output: endpoint { - remote-endpoint = <&panel_input>; - }; - }; -}; diff --git a/arch/mips/boot/dts/ingenic/rs97.dts b/arch/mips/boot/dts/ingenic/rs97.dts new file mode 100644 index 00000000000000..6f9ebe5f5264ff --- /dev/null +++ b/arch/mips/boot/dts/ingenic/rs97.dts @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include "jz4760.dtsi" +#include + +#include +#include +#include + +/ { + compatible = "ylm,rs97", "ingenic,jz4760"; + model = "RS-97"; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + }; + + memory: memory { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + chosen { + stdout-path = "serial1:57600n8"; + }; + + vcc: regulator-0 { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + lcd_power: regulator-1 { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + gpio = <&gpc 9 0>; + enable-active-high; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + mmc2_power: regulator-2 { + compatible = "regulator-fixed"; + regulator-name = "mmc2_vcc"; + gpio = <&gpf 3 0>; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc>; + }; + + audio_amp: analog-amplifier { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpe 9 0>; + enable-delay-ms = <50>; + + VCC-supply = <&vcc>; + }; + + sound_card: sound { + compatible = "simple-audio-card"; + + simple-audio-card,name = "lepus-audio"; + simple-audio-card,format = "i2s"; + + simple-audio-card,widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; + simple-audio-card,routing = + "Headphones", "LHPOUT", + "Headphones", "RHPOUT", + "INL", "LHPOUT", + "INR", "RHPOUT", + "Speaker", "OUTL", + "Speaker", "OUTR"; + simple-audio-card,pin-switches = "Speaker"; + + simple-audio-card,hp-det-gpio = <&gpd 6 GPIO_ACTIVE_LOW>; + simple-audio-card,aux-devs = <&audio_amp>; + + simple-audio-card,bitclock-master = <&dai_codec>; + simple-audio-card,frame-master = <&dai_codec>; + + dai_cpu: simple-audio-card,cpu { + sound-dai = <&aic>; + }; + + dai_codec: simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; + + backlight: backlight { + compatible = "pwm-backlight"; + pwms = <&pwm 1 40000 0>; + power-supply = <&vcc>; + + brightness-levels = <0 16 32 48 64 80 96 112 128 + 144 160 176 192 208 224 240 255>; + default-brightness-level = <12>; + + pinctrl-names = "init", "sleep", "default"; + pinctrl-0 = <&pins_pwm1_sleep>; + pinctrl-1 = <&pins_pwm1_sleep>; + pinctrl-2 = <&pins_pwm1>; + }; + + gpio_keys: gpio-keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + + autorepeat; + + btn_up: button-0 { + label = "D-pad up"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 25 GPIO_ACTIVE_LOW>; + }; + + btn_down: button-1 { + label = "D-pad down"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 24 GPIO_ACTIVE_LOW>; + }; + + btn_left: button-2 { + label = "D-pad left"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 0 GPIO_ACTIVE_LOW>; + }; + + btn_right: button-3 { + label = "D-pad right"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 26 GPIO_ACTIVE_LOW>; + }; + + btn_a: button-4 { + label = "Button A"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 22 GPIO_ACTIVE_LOW>; + }; + + btn_b: button-5 { + label = "Button B"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 23 GPIO_ACTIVE_LOW>; + }; + + btn_y: button-6 { + label = "Button Y"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 11 GPIO_ACTIVE_LOW>; + }; + + btn_x: button-7 { + label = "Button X"; + linux,code = ; + linux,can-disable; + gpios = <&gpe 7 GPIO_ACTIVE_LOW>; + }; + + btn_ltrig: button-8 { + label = "Left shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpb 23 GPIO_ACTIVE_LOW>; + }; + + btn_rtrig: button-9 { + label = "Right shoulder button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 24 GPIO_ACTIVE_LOW>; + }; + + button-10 { + label = "Start button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 18 GPIO_ACTIVE_HIGH>; + }; + + button-11 { + label = "Select button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 17 GPIO_ACTIVE_HIGH>; + }; + + btn_power: button-12 { + label = "Power button"; + linux,code = ; + linux,can-disable; + gpios = <&gpa 30 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + + btn_star: button-13 { + label = "Star button"; + linux,code = ; + linux,can-disable; + gpios = <&gpd 21 GPIO_ACTIVE_LOW>; + wakeup-source; + }; + }; + + spi: spi { + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = <&gpd 11 GPIO_ACTIVE_HIGH>; + mosi-gpios = <&gpe 2 GPIO_ACTIVE_HIGH>; + cs-gpios = <&gpe 0 GPIO_ACTIVE_LOW>; + num-chipselects = <1>; + + panel: lcd-panel@0 { + compatible = "auo,a030jtn01"; + reg = <0>; + + spi-max-frequency = <10000000>; + spi-cpol; + spi-cpha; + spi-3wire; + + reset-gpios = <&gpe 4 GPIO_ACTIVE_LOW>; + power-supply = <&lcd_power>; + + backlight = <&backlight>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_lcd>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; + }; + + usb_conn: connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + label = "mini-USB"; + type = "mini"; + + vbus-gpios = <&gpd 7 GPIO_ACTIVE_HIGH>; + //id-gpios = <&gpa 11 GPIO_ACTIVE_HIGH>; + //vbus-supply = <&otg_vbus>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_otg>; + + port { + usb_ep: endpoint { + remote-endpoint = <&usb_otg_ep>; + }; + }; + }; + + battery: battery { + compatible = "simple-battery"; + voltage-min-design-microvolt = <3400000>; + voltage-max-design-microvolt = <4200000>; + }; + + vdiv: voltage-divider { + compatible = "voltage-divider"; + #io-channel-cells = <0>; + + output-ohms = <332000>; + full-ohms = <1332000>; + + io-channels = <&adc INGENIC_ADC_BATTERY>; + }; + + pmu: pmu { + compatible = "ingenic,jz4760-battery", "ingenic,jz4740-battery"; + io-channels = <&vdiv>; + io-channel-names = "battery"; + monitored-battery = <&battery>; + + power-supplies = <&usb_conn>; + }; + + cpu_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-480000000 { opp-hz = /bits/ 64 <480000000>; }; + opp-528000000 { opp-hz = /bits/ 64 <528000000>; }; + opp-576000000 { opp-hz = /bits/ 64 <576000000>; }; + opp-624000000 { opp-hz = /bits/ 64 <624000000>; }; + opp-672000000 { opp-hz = /bits/ 64 <672000000>; }; + opp-720000000 { opp-hz = /bits/ 64 <720000000>; }; + opp-768000000 { opp-hz = /bits/ 64 <768000000>; }; + opp-816000000 { opp-hz = /bits/ 64 <816000000>; }; + opp-864000000 { opp-hz = /bits/ 64 <864000000>; }; + opp-912000000 { opp-hz = /bits/ 64 <912000000>; }; + opp-960000000 { opp-hz = /bits/ 64 <960000000>; }; + opp-1008000000 { opp-hz = /bits/ 64 <1008000000>; }; + opp-1056000000 { opp-hz = /bits/ 64 <1056000000>; }; + opp-1104000000 { opp-hz = /bits/ 64 <1104000000>; }; + opp-1152000000 { opp-hz = /bits/ 64 <1152000000>; }; + opp-1200000000 { opp-hz = /bits/ 64 <1200000000>; }; + opp-1248000000 { opp-hz = /bits/ 64 <1248000000>; }; + opp-1296000000 { opp-hz = /bits/ 64 <1296000000>; }; + opp-1344000000 { opp-hz = /bits/ 64 <1344000000>; }; + opp-1392000000 { opp-hz = /bits/ 64 <1392000000>; }; + opp-1440000000 { opp-hz = /bits/ 64 <1440000000>; }; + opp-1488000000 { opp-hz = /bits/ 64 <1488000000>; }; + opp-1536000000 { opp-hz = /bits/ 64 <1536000000>; }; + opp-1584000000 { opp-hz = /bits/ 64 <1584000000>; }; + }; +}; + +&cpu0 { + operating-points-v2 = <&cpu_opp_table>; + + /* We use the main PLL as the CPU clock for the cpufreq driver. */ + clocks = <&cgu JZ4760_CLK_PLL0>; +}; + +&ext { + clock-frequency = <12000000>; +}; + +&pinctrl { + pins_lcd: lcd { + function = "lcd"; + groups = "lcd-8bit"; + }; + + pins_uart1: uart1 { + function = "uart1"; + groups = "uart1-data"; + }; + + pins_mmc0: mmc0 { + function = "mmc0"; + groups = "mmc0-1bit-a", "mmc0-4bit-a"; + }; + + pins_mmc2: mmc2 { + function = "mmc2"; + groups = "mmc2-1bit-e", "mmc2-4bit-e"; + }; + + pwm1 { + pins_pwm1_sleep: pwm1-sleep { + pins = "PE1"; + output-low; + }; + + pins_pwm1: pwm1-default { + function = "pwm1"; + groups = "pwm1"; + }; + }; + + pins_otg: otg { + otg-vbus-pin { + function = "otg"; + groups = "otg-vbus"; + }; + + vbus_pin: vbus-pin { + pins = "PD7"; + bias-disable; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pins_uart1>; + + status = "okay"; +}; + +&cgu { + /* + * Put high-speed peripherals under PLL1, such that we can change the + * PLL0 frequency on demand without having to suspend peripherals. + * We use a rate of 432 MHz, which is the least common multiple of + * 27 MHz (required by TV encoder) and 48 MHz (required by USB host). + * Use the 32 kHz oscillator as the parent of the RTC for a higher + * precision. + */ + assigned-clocks = + <&cgu JZ4760_CLK_PLL1>, + <&cgu JZ4760_CLK_PLL0_HALF>, + <&cgu JZ4760_CLK_MMC_MUX>, + <&cgu JZ4760_CLK_RTC>, + <&cgu JZ4760_CLK_UHC>, + <&cgu JZ4760_CLK_LPCLK_DIV>; + assigned-clock-parents = + <0>, + <0>, + <&cgu JZ4760_CLK_PLL0_HALF>, + <&cgu JZ4760_CLK_OSC32K>, + <&cgu JZ4760_CLK_PLL1>, + <&cgu JZ4760_CLK_PLL1>; + assigned-clock-rates = + <432000000>, <600000000>, <50000000>; +}; + +&tcu { + /* + * 750 kHz for the system timer and clocksource, 12 MHz for the OST, + * and use RTC as the parent for the watchdog clock + */ + assigned-clocks = <&tcu TCU_CLK_TIMER0>, <&tcu TCU_CLK_TIMER2>, + <&tcu TCU_CLK_OST>, <&tcu TCU_CLK_WDT>; + assigned-clock-parents = <0>, <0>, <0>, <&cgu JZ4760_CLK_RTC>; + assigned-clock-rates = <750000>, <750000>, <12000000>; + + /* PWM1 is in use, so use channel #2 for the clocksource */ + ingenic,pwm-channels-mask = <0xfa>; +}; + +&usb_otg { + port { + usb_otg_ep: endpoint { + remote-endpoint = <&usb_ep>; + }; + }; +}; + +&otg_phy { + vcc-supply = <&vcc>; +}; + +&rtc { + clocks = <&cgu JZ4760_CLK_RTC>; + clock-names = "rtc"; + + system-power-controller; + + ingenic,reset-pin-assert-time-ms = <125>; + ingenic,min-wakeup-pin-assert-time-ms = <500>; +}; + +&mmc0 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + vmmc-supply = <&vcc>; + non-removable; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc0>; +}; + +&mmc2 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + cd-gpios = <&gpf 0 GPIO_ACTIVE_LOW>; + vmmc-supply = <&mmc2_power>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc2>; +}; + +&lcd { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + panel_output: endpoint { + remote-endpoint = <&panel_input>; + }; + }; + + port@8 { + reg = <8>; + + ipu_output: endpoint { + remote-endpoint = <&ipu_input>; + }; + }; + }; +}; + +&ipu { + port { + ipu_input: endpoint { + remote-endpoint = <&ipu_output>; + }; + }; +}; diff --git a/arch/mips/generic/Platform b/arch/mips/generic/Platform index 0c03623f38970f..7ec2828680ac2b 100644 --- a/arch/mips/generic/Platform +++ b/arch/mips/generic/Platform @@ -12,7 +12,7 @@ cflags-$(CONFIG_MACH_INGENIC_SOC) += -I$(srctree)/arch/mips/include/asm/mach-ingenic cflags-$(CONFIG_MIPS_GENERIC) += -I$(srctree)/arch/mips/include/asm/mach-generic -load-$(CONFIG_MIPS_GENERIC) += 0xffffffff80100000 +load-$(CONFIG_MIPS_GENERIC) += $(if $(CONFIG_LD_IS_LLD),0x80100000,0xffffffff80100000) all-$(CONFIG_MIPS_GENERIC) += vmlinux.gz.itb its-y := vmlinux.its.S diff --git a/arch/mips/generic/board-ingenic.c b/arch/mips/generic/board-ingenic.c index c422bbc890eda3..1f4906875e7bc2 100644 --- a/arch/mips/generic/board-ingenic.c +++ b/arch/mips/generic/board-ingenic.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -60,6 +61,50 @@ static __init char *ingenic_get_system_type(unsigned long machtype) } } +#define INGENIC_CGU_BASE 0x10000000 +#define JZ4750_CGU_CPCCR_ECS BIT(30) +#define JZ4760_CGU_CPCCR_ECS BIT(31) + +static __init void ingenic_force_12M_ext(const void *fdt, unsigned int mask) +{ + const __be32 *prop; + unsigned int cpccr; + void __iomem *cgu; + bool use_div; + int offset; + + offset = fdt_path_offset(fdt, "/ext"); + if (offset < 0) + return; + + prop = fdt_getprop(fdt, offset, "clock-frequency", NULL); + if (!prop) + return; + + /* + * If the external oscillator is 24 MHz, enable the /2 divider to + * drive it down to 12 MHz, since this is what the hardware can work + * with. + * The 16 MHz cutoff value is arbitrary; setting it to 12 MHz would not + * work as the crystal frequency (as reported in the Device Tree) might + * be slightly above this value. + */ + use_div = be32_to_cpup(prop) >= 16000000; + + cgu = ioremap(INGENIC_CGU_BASE, 0x4); + if (!cgu) + return; + + cpccr = ioread32(cgu); + if (use_div) + cpccr |= mask; + else + cpccr &= ~mask; + iowrite32(cpccr, cgu); + + iounmap(cgu); +} + static __init const void *ingenic_fixup_fdt(const void *fdt, const void *match_data) { /* @@ -73,6 +118,18 @@ static __init const void *ingenic_fixup_fdt(const void *fdt, const void *match_d mips_machtype = (unsigned long)match_data; system_type = ingenic_get_system_type(mips_machtype); + switch (mips_machtype) { + case MACH_INGENIC_JZ4750: + case MACH_INGENIC_JZ4755: + ingenic_force_12M_ext(fdt, JZ4750_CGU_CPCCR_ECS); + break; + case MACH_INGENIC_JZ4760: + ingenic_force_12M_ext(fdt, JZ4760_CGU_CPCCR_ECS); + break; + default: + break; + } + return fdt; } @@ -117,14 +174,14 @@ static void ingenic_halt(void) ingenic_wait_instr(); } -static int __maybe_unused ingenic_pm_enter(suspend_state_t state) +static int ingenic_pm_enter(suspend_state_t state) { ingenic_wait_instr(); return 0; } -static const struct platform_suspend_ops ingenic_pm_ops __maybe_unused = { +static const struct platform_suspend_ops ingenic_pm_ops = { .valid = suspend_valid_only_mem, .enter = ingenic_pm_enter, }; diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index 43d1faa02933c8..c29dbc8c1d491e 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h @@ -272,6 +272,27 @@ enum lx_func { lbx_op = 0x16, }; +/* + * func field for special2 MXU opcodes (Ingenic XBurst MXU). + */ +enum mxu_func { + /* TODO, other MXU funcs */ + mxu_lx_op = 0x28, +}; + +/* + * op field for special2 MXU LX opcodes (Ingenic XBurst MXU). + */ +enum lx_ingenic_func { + mxu_lxb_op, + mxu_lxh_op, + /* reserved */ + mxu_lxw_op = 3, + mxu_lxbu_op, + mxu_lxhu_op, + /* more reserved */ +}; + /* * BSHFL opcodes */ @@ -774,6 +795,17 @@ struct dsp_format { /* SPEC3 DSP format instructions */ ;)))))) }; +struct mxu_lx_format { /* SPEC2 MXU LX format instructions */ + __BITFIELD_FIELD(unsigned int opcode : 6, + __BITFIELD_FIELD(unsigned int rs : 5, + __BITFIELD_FIELD(unsigned int rt : 5, + __BITFIELD_FIELD(unsigned int rd : 5, + __BITFIELD_FIELD(unsigned int strd : 2, + __BITFIELD_FIELD(unsigned int op : 3, + __BITFIELD_FIELD(unsigned int func : 6, + ;))))))) +}; + struct spec3_format { /* SPEC3 */ __BITFIELD_FIELD(unsigned int opcode:6, __BITFIELD_FIELD(unsigned int rs:5, @@ -1125,6 +1157,7 @@ union mips_instruction { struct loongson3_lswc2_format loongson3_lswc2_format; struct loongson3_lsdc2_format loongson3_lsdc2_format; struct loongson3_lscsr_format loongson3_lscsr_format; + struct mxu_lx_format mxu_lx_format; }; union mips16e_instruction { diff --git a/arch/mips/ingenic/Kconfig b/arch/mips/ingenic/Kconfig index f595b339a4b847..440e05f2915a08 100644 --- a/arch/mips/ingenic/Kconfig +++ b/arch/mips/ingenic/Kconfig @@ -5,6 +5,7 @@ config MACH_INGENIC_GENERIC select MACH_INGENIC select MACH_JZ4740 select MACH_JZ4725B + select MACH_JZ4760 select MACH_JZ4770 select MACH_JZ4780 select MACH_X1000 @@ -27,6 +28,10 @@ config JZ4740_RS90 bool "YLM RetroMini (RS-90)" select MACH_JZ4725B +config JZ4760_RS97 + bool "YLM RS-97" + select MACH_JZ4760 + config JZ4770_GCW0 bool "Game Consoles Worldwide GCW Zero" select MACH_JZ4770 @@ -53,6 +58,10 @@ config MACH_JZ4740 bool select SYS_HAS_CPU_MIPS32_R1 +config MACH_JZ4760 + bool + select SYS_HAS_CPU_MIPS32_R1 + config MACH_JZ4770 bool select MIPS_CPU_SCACHE diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 7b5aba5df02ebd..f4cf94e92ec3ab 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -160,6 +160,47 @@ static void emulate_load_store_insn(struct pt_regs *regs, * The remaining opcodes are the ones that are really of * interest. */ +#ifdef CONFIG_MACH_INGENIC + case spec2_op: + if (insn.mxu_lx_format.func != mxu_lx_op) + goto sigbus; /* other MXU instructions we don't care */ + + switch (insn.mxu_lx_format.op) { + case mxu_lxw_op: + if (user && !access_ok(addr, 4)) + goto sigbus; + LoadW(addr, value, res); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.mxu_lx_format.rd] = value; + break; + case mxu_lxh_op: + if (user && !access_ok(addr, 2)) + goto sigbus; + LoadHW(addr, value, res); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.dsp_format.rd] = value; + break; + case mxu_lxhu_op: + if (user && !access_ok(addr, 2)) + goto sigbus; + LoadHWU(addr, value, res); + if (res) + goto fault; + compute_return_epc(regs); + regs->regs[insn.dsp_format.rd] = value; + break; + case mxu_lxb_op: + case mxu_lxbu_op: + goto sigbus; + default: + goto sigill; + } + break; +#endif case spec3_op: if (insn.dsp_format.func == lx_op) { switch (insn.dsp_format.op) { diff --git a/arch/openrisc/configs/or1ksim_defconfig b/arch/openrisc/configs/or1ksim_defconfig index 6e1e004047c75f..0116e465238f68 100644 --- a/arch/openrisc/configs/or1ksim_defconfig +++ b/arch/openrisc/configs/or1ksim_defconfig @@ -10,7 +10,8 @@ CONFIG_EXPERT=y # CONFIG_AIO is not set # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_MODULES=y # CONFIG_BLOCK is not set CONFIG_OPENRISC_BUILTIN_DTB="or1ksim" diff --git a/arch/openrisc/configs/simple_smp_defconfig b/arch/openrisc/configs/simple_smp_defconfig index ff49d868e04013..b990cb6c930926 100644 --- a/arch/openrisc/configs/simple_smp_defconfig +++ b/arch/openrisc/configs/simple_smp_defconfig @@ -16,7 +16,8 @@ CONFIG_EXPERT=y # CONFIG_AIO is not set # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_MODULES=y # CONFIG_BLOCK is not set CONFIG_OPENRISC_BUILTIN_DTB="simple_smp" diff --git a/arch/riscv/configs/nommu_k210_defconfig b/arch/riscv/configs/nommu_k210_defconfig index 96fe8def644ce6..79b3ccd58ff031 100644 --- a/arch/riscv/configs/nommu_k210_defconfig +++ b/arch/riscv/configs/nommu_k210_defconfig @@ -25,7 +25,8 @@ CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_MMU is not set CONFIG_SOC_CANAAN=y CONFIG_NONPORTABLE=y diff --git a/arch/riscv/configs/nommu_k210_sdcard_defconfig b/arch/riscv/configs/nommu_k210_sdcard_defconfig index 37974065437326..6b80bb13b8edd9 100644 --- a/arch/riscv/configs/nommu_k210_sdcard_defconfig +++ b/arch/riscv/configs/nommu_k210_sdcard_defconfig @@ -17,7 +17,8 @@ CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_MMU is not set CONFIG_SOC_CANAAN=y CONFIG_NONPORTABLE=y diff --git a/arch/riscv/configs/nommu_virt_defconfig b/arch/riscv/configs/nommu_virt_defconfig index 1a56eda5ce46db..4cf0f297091e72 100644 --- a/arch/riscv/configs/nommu_virt_defconfig +++ b/arch/riscv/configs/nommu_virt_defconfig @@ -22,7 +22,8 @@ CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_MMU is not set CONFIG_SOC_VIRT=y CONFIG_NONPORTABLE=y diff --git a/arch/sh/configs/rsk7201_defconfig b/arch/sh/configs/rsk7201_defconfig index 619c1869945976..376e95fa77bc68 100644 --- a/arch/sh/configs/rsk7201_defconfig +++ b/arch/sh/configs/rsk7201_defconfig @@ -10,7 +10,8 @@ CONFIG_USER_NS=y CONFIG_PID_NS=y CONFIG_BLK_DEV_INITRD=y # CONFIG_AIO is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_PROFILING=y CONFIG_MODULES=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/sh/configs/rsk7203_defconfig b/arch/sh/configs/rsk7203_defconfig index d00fafc021e1ac..1d5fd67a3949b4 100644 --- a/arch/sh/configs/rsk7203_defconfig +++ b/arch/sh/configs/rsk7203_defconfig @@ -11,7 +11,8 @@ CONFIG_USER_NS=y CONFIG_PID_NS=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_PROFILING=y CONFIG_MODULES=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig index 122216123e6399..78e0e7be57ee1f 100644 --- a/arch/sh/configs/se7206_defconfig +++ b/arch/sh/configs/se7206_defconfig @@ -21,7 +21,8 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y # CONFIG_ELF_CORE is not set # CONFIG_COMPAT_BRK is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_PROFILING=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/sh/configs/shmin_defconfig b/arch/sh/configs/shmin_defconfig index c0b6f40d01cca5..e078b193a78a89 100644 --- a/arch/sh/configs/shmin_defconfig +++ b/arch/sh/configs/shmin_defconfig @@ -9,7 +9,8 @@ CONFIG_LOG_BUF_SHIFT=14 # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set # CONFIG_SHMEM is not set -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y # CONFIG_BLK_DEV_BSG is not set CONFIG_CPU_SUBTYPE_SH7706=y CONFIG_MEMORY_START=0x0c000000 diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig index 32ec6eb1eabcfc..aa353dff7f1931 100644 --- a/arch/sh/configs/shx3_defconfig +++ b/arch/sh/configs/shx3_defconfig @@ -20,7 +20,8 @@ CONFIG_USER_NS=y CONFIG_PID_NS=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_KALLSYMS_ALL=y -CONFIG_SLOB=y +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y CONFIG_PROFILING=y CONFIG_KPROBES=y CONFIG_MODULES=y diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 57b83665e5c3a4..b167c129b1d889 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2631,6 +2631,15 @@ struct clk *clk_get_parent(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_parent); +struct clk *clk_get_first_to_set_rate(struct clk *clk) +{ + while (clk && (clk->core->flags & CLK_SET_RATE_PARENT)) + clk = clk_get_parent(clk); + + return clk; +} +EXPORT_SYMBOL_GPL(clk_get_first_to_set_rate); + static struct clk_core *__clk_init_parent(struct clk_core *core) { u8 index = 0; diff --git a/drivers/clk/ingenic/jz4760-cgu.c b/drivers/clk/ingenic/jz4760-cgu.c index ecd395ac8a28d4..e407f00bd59422 100644 --- a/drivers/clk/ingenic/jz4760-cgu.c +++ b/drivers/clk/ingenic/jz4760-cgu.c @@ -58,7 +58,7 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, unsigned long rate, unsigned long parent_rate, unsigned int *pm, unsigned int *pn, unsigned int *pod) { - unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 2; + unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1; /* The frequency after the N divider must be between 1 and 50 MHz. */ n = parent_rate / (1 * MHZ); @@ -66,19 +66,17 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, /* The N divider must be >= 2. */ n = clamp_val(n, 2, 1 << pll_info->n_bits); - for (;; n >>= 1) { - od = (unsigned int)-1; + rate /= MHZ; + parent_rate /= MHZ; - do { - m = (rate / MHZ) * (1 << ++od) * n / (parent_rate / MHZ); - } while ((m > m_max || m & 1) && (od < 4)); - - if (od < 4 && m >= 4 && m <= m_max) - break; + for (m = m_max; m >= m_max && n >= 2; n--) { + m = rate * n / parent_rate; + od = m & 1; + m <<= od; } *pm = m; - *pn = n; + *pn = n + 1; *pod = 1 << od; } diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 4f6f1deba28c6f..b3486087162734 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -35,10 +35,6 @@ #define IT66121_DEVICE_ID0_REG 0x02 #define IT66121_DEVICE_ID1_REG 0x03 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 #define IT66121_REVISION_MASK GENMASK(7, 4) #define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) @@ -72,6 +68,7 @@ #define IT66121_AFE_XP_ENO BIT(4) #define IT66121_AFE_XP_RESETB BIT(3) #define IT66121_AFE_XP_PWDI BIT(2) +#define IT6610_AFE_XP_BYPASS BIT(0) #define IT66121_AFE_IP_REG 0x64 #define IT66121_AFE_IP_GAINBIT BIT(7) @@ -286,13 +283,18 @@ #define IT66121_AUD_SWL_16BIT 0x2 #define IT66121_AUD_SWL_NOT_INDICATED 0x0 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 -#define IT66121_DEVICE_MASK 0x0F #define IT66121_AFE_CLK_HIGH 80000 /* Khz */ +enum chip_id { + ID_IT6610, + ID_IT66121, +}; + +struct it66121_chip_info { + enum chip_id id; + u16 vid, pid; +}; + struct it66121_ctx { struct regmap *regmap; struct drm_bridge bridge; @@ -301,7 +303,6 @@ struct it66121_ctx { struct device *dev; struct gpio_desc *gpio_reset; struct i2c_client *client; - struct regulator_bulk_data supplies[3]; u32 bus_width; struct mutex lock; /* Protects fields below and device registers */ struct hdmi_avi_infoframe hdmi_avi_infoframe; @@ -312,6 +313,7 @@ struct it66121_ctx { u8 swl; bool auto_cts; } audio; + const struct it66121_chip_info *info; }; static const struct regmap_range_cfg it66121_regmap_banks[] = { @@ -342,16 +344,6 @@ static void it66121_hw_reset(struct it66121_ctx *ctx) gpiod_set_value(ctx->gpio_reset, 0); } -static inline int ite66121_power_on(struct it66121_ctx *ctx) -{ - return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - -static inline int ite66121_power_off(struct it66121_ctx *ctx) -{ - return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - static inline int it66121_preamble_ddc(struct it66121_ctx *ctx) { return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); @@ -406,16 +398,22 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, + IT66121_AFE_IP_ER0, IT66121_AFE_IP_GAINBIT); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, 0x80); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, 0x80); + if (ret) + return ret; + } } else { ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, IT66121_AFE_XP_GAINBIT | @@ -426,17 +424,24 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1); + IT66121_AFE_IP_ER0, + IT66121_AFE_IP_ER0); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, - IT66121_AFE_XP_EC1_LOWCLK); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, + IT66121_AFE_IP_EC1); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, + IT66121_AFE_XP_EC1_LOWCLK); + if (ret) + return ret; + } } /* Clear reset flags */ @@ -445,38 +450,36 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, if (ret) return ret; + if (ctx->info->id == ID_IT6610) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT6610_AFE_XP_BYPASS, + IT6610_AFE_XP_BYPASS); + if (ret) + return ret; + } + return it66121_fire_afe(ctx); } static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) { int ret, val; - u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | - IT66121_DDC_STATUS_ARBI_LOSE; + u32 error = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | + IT66121_DDC_STATUS_ARBI_LOSE; + u32 done = IT66121_DDC_STATUS_TX_DONE; - ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, - IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); + ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, + val & (error | done), IT66121_EDID_SLEEP_US, + IT66121_EDID_TIMEOUT_US); if (ret) return ret; - if (val & busy) + if (val & error) return -EAGAIN; return 0; } -static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx) -{ - int ret; - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, - IT66121_DDC_COMMAND_FIFO_CLR); -} - static int it66121_abort_ddc_ops(struct it66121_ctx *ctx) { int ret; @@ -516,7 +519,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, unsigned int block, size_t len) { struct it66121_ctx *ctx = context; - unsigned int val; int remain = len; int offset = 0; int ret, cnt; @@ -524,26 +526,9 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset = (block % 2) * len; block = block / 2; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_clear_ddc_fifo(ctx); - if (ret) - return ret; - while (remain > 0) { cnt = (remain > IT66121_EDID_FIFO_SIZE) ? IT66121_EDID_FIFO_SIZE : remain; - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, IT66121_DDC_COMMAND_FIFO_CLR); @@ -554,25 +539,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, if (ret) return ret; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, - IT66121_DDC_HEADER_EDID); - if (ret) - return ret; - ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset); if (ret) return ret; @@ -593,20 +559,18 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset += cnt; remain -= cnt; - /* Per programming manual, sleep here before emptying the FIFO */ - msleep(20); - ret = it66121_wait_ddc_ready(ctx); + if (ret) { + it66121_abort_ddc_ops(ctx); + return ret; + } + + ret = regmap_noinc_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, + buf, cnt); if (ret) return ret; - do { - ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val); - if (ret) - return ret; - *(buf++) = val; - cnt--; - } while (cnt > 0); + buf += cnt; } return 0; @@ -635,10 +599,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_RCLK, 0); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_RCLK, 0); + if (ret) + return ret; + } ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG, IT66121_INT_TX_CLK_OFF, 0); @@ -684,11 +650,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, /* Per programming manual, sleep here for bridge to settle */ msleep(50); - /* Start interrupts */ - return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, - IT66121_INT_MASK1_DDC_NOACK | - IT66121_INT_MASK1_DDC_FIFOERR | - IT66121_INT_MASK1_DDC_BUSHANG, 0); + return 0; } static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) @@ -780,29 +742,32 @@ static void it66121_bridge_disable(struct drm_bridge *bridge, ctx->connector = NULL; } +static int it66121_bridge_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + if (ctx->info->id == ID_IT6610) { + /* The IT6610 only supports these settings */ + bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + bridge_state->input_bus_cfg.flags &= + ~DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; + } + + return 0; +} + static void it66121_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - int ret, i; u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { - IT66121_AVIINFO_DB1_REG, - IT66121_AVIINFO_DB2_REG, - IT66121_AVIINFO_DB3_REG, - IT66121_AVIINFO_DB4_REG, - IT66121_AVIINFO_DB5_REG, - IT66121_AVIINFO_DB6_REG, - IT66121_AVIINFO_DB7_REG, - IT66121_AVIINFO_DB8_REG, - IT66121_AVIINFO_DB9_REG, - IT66121_AVIINFO_DB10_REG, - IT66121_AVIINFO_DB11_REG, - IT66121_AVIINFO_DB12_REG, - IT66121_AVIINFO_DB13_REG - }; + int ret; mutex_lock(&ctx->lock); @@ -822,10 +787,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, } /* Write new AVI infoframe packet */ - for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { - if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) - goto unlock; - } + ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, + &buf[HDMI_INFOFRAME_HEADER_SIZE], + HDMI_AVI_INFOFRAME_SIZE); + if (ret) + goto unlock; + if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) goto unlock; @@ -838,9 +805,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) goto unlock; - if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, + IT66121_CLK_BANK_PWROFF_TXCLK)) { goto unlock; + } if (it66121_configure_input(ctx)) goto unlock; @@ -848,7 +818,11 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (it66121_configure_afe(ctx, adjusted_mode)) goto unlock; - regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { + goto unlock; + } unlock: mutex_unlock(&ctx->lock); @@ -906,9 +880,25 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, { struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); struct edid *edid; + int ret; mutex_lock(&ctx->lock); + ret = it66121_preamble_ddc(ctx); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + + ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, + IT66121_DDC_HEADER_EDID); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); + +out_unlock: mutex_unlock(&ctx->lock); return edid; @@ -923,6 +913,7 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, .atomic_enable = it66121_bridge_enable, .atomic_disable = it66121_bridge_disable, + .atomic_check = it66121_bridge_check, .mode_set = it66121_bridge_mode_set, .mode_valid = it66121_bridge_mode_valid, .detect = it66121_bridge_detect, @@ -952,21 +943,14 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); if (ret) { dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); - } else { - if (val & IT66121_INT_STATUS1_DDC_FIFOERR) - it66121_clear_ddc_fifo(ctx); - if (val & (IT66121_INT_STATUS1_DDC_BUSHANG | - IT66121_INT_STATUS1_DDC_NOACK)) - it66121_abort_ddc_ops(ctx); - if (val & IT66121_INT_STATUS1_HPD_STATUS) { - regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, - IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); + } else if (val & IT66121_INT_STATUS1_HPD_STATUS) { + regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, + IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); - status = it66121_is_hpd_detect(ctx) ? connector_status_connected - : connector_status_disconnected; + status = it66121_is_hpd_detect(ctx) ? connector_status_connected + : connector_status_disconnected; - event = true; - } + event = true; } regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, @@ -1512,9 +1496,13 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) return PTR_ERR_OR_ZERO(ctx->audio.pdev); } -static int it66121_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const char * const it66121_supplies[] = { + "vcn33", "vcn18", "vrf12" +}; + +static int it66121_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; struct device_node *ep; int ret; @@ -1536,6 +1524,7 @@ static int it66121_probe(struct i2c_client *client, ctx->dev = dev; ctx->client = client; + ctx->info = (const struct it66121_chip_info *) id->driver_data; of_property_read_u32(ep, "bus-width", &ctx->bus_width); of_node_put(ep); @@ -1565,26 +1554,18 @@ static int it66121_probe(struct i2c_client *client, i2c_set_clientdata(client, ctx); mutex_init(&ctx->lock); - ctx->supplies[0].supply = "vcn33"; - ctx->supplies[1].supply = "vcn18"; - ctx->supplies[2].supply = "vrf12"; - ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it66121_supplies), + it66121_supplies); if (ret) { - dev_err(ctx->dev, "regulator_bulk failed\n"); + dev_err(dev, "Failed to enable power supplies\n"); return ret; } - ret = ite66121_power_on(ctx); - if (ret) - return ret; - it66121_hw_reset(ctx); ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); - if (IS_ERR(ctx->regmap)) { - ite66121_power_off(ctx); + if (IS_ERR(ctx->regmap)) return PTR_ERR(ctx->regmap); - } regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); @@ -1595,9 +1576,8 @@ static int it66121_probe(struct i2c_client *client, revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); device_ids[1] &= IT66121_DEVICE_ID1_MASK; - if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || - device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { - ite66121_power_off(ctx); + if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid || + (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) { return -ENODEV; } @@ -1610,7 +1590,6 @@ static int it66121_probe(struct i2c_client *client, IRQF_ONESHOT, dev_name(dev), ctx); if (ret < 0) { dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); - ite66121_power_off(ctx); return ret; } @@ -1627,19 +1606,32 @@ static void it66121_remove(struct i2c_client *client) { struct it66121_ctx *ctx = i2c_get_clientdata(client); - ite66121_power_off(ctx); drm_bridge_remove(&ctx->bridge); mutex_destroy(&ctx->lock); } static const struct of_device_id it66121_dt_match[] = { { .compatible = "ite,it66121" }, + { .compatible = "ite,it6610" }, { } }; MODULE_DEVICE_TABLE(of, it66121_dt_match); +static const struct it66121_chip_info it66121_chip_info = { + .id = ID_IT66121, + .vid = 0x4954, + .pid = 0x0612, +}; + +static const struct it66121_chip_info it6610_chip_info = { + .id = ID_IT6610, + .vid = 0xca00, + .pid = 0x0611, +}; + static const struct i2c_device_id it66121_id[] = { - { "it66121", 0 }, + { "it66121", (kernel_ulong_t) &it66121_chip_info }, + { "it6610", (kernel_ulong_t) &it6610_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, it66121_id); @@ -1649,7 +1641,7 @@ static struct i2c_driver it66121_driver = { .name = "it66121", .of_match_table = it66121_dt_match, }, - .probe = it66121_probe, + .probe_new = it66121_probe, .remove = it66121_remove, .id_table = it66121_id, }; diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index a53f475d33df32..844e4bff3f3bbb 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -18,7 +18,7 @@ config DRM_INGENIC if DRM_INGENIC config DRM_INGENIC_IPU - bool "IPU support for Ingenic SoCs" + tristate "IPU support for Ingenic SoCs" help Choose this option to enable support for the IPU found in Ingenic SoCs. diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile index f10cc1c5a5f226..e650d4ec895bec 100644 --- a/drivers/gpu/drm/ingenic/Makefile +++ b/drivers/gpu/drm/ingenic/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o ingenic-drm-y = ingenic-drm-drv.o -ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o +obj-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o obj-$(CONFIG_DRM_INGENIC_DW_HDMI) += ingenic-dw-hdmi.o diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index ab0515d2c420a2..0c36af0a9bc11e 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -76,9 +76,32 @@ struct jz_soc_info { unsigned int num_formats_f0, num_formats_f1; }; +struct ingenic_gem_object { + struct drm_gem_dma_object base; + struct ingenic_dma_hwdesc *hwdescs; + dma_addr_t hwdescs_phys; +}; + struct ingenic_drm_private_state { struct drm_private_state base; bool use_palette; + + /* + * A lot of devices with an Ingenic SoC have a weird LCD panel attached, + * where the pixels are not square. For instance, the AUO A030JTN01 and + * Innolux EJ030NA panels have a resolution of 320x480 with a 4:3 aspect + * ratio. + * + * All userspace applications are built with the assumption that the + * pixels are square. To be able to support these devices without too + * much effort, add a doublescan feature, which allows the f0 and f1 + * planes to be used with only half of the screen's vertical resolution, + * where each line of the input is displayed twice. + * + * This is done using a chained list of DMA descriptors, one descriptor + * per output line. + */ + bool doublescan; }; struct ingenic_drm { @@ -202,6 +225,11 @@ static inline struct ingenic_drm *drm_nb_get_priv(struct notifier_block *nb) return container_of(nb, struct ingenic_drm, clock_nb); } +static inline struct ingenic_gem_object *to_ingenic_gem_obj(struct drm_gem_object *gem_obj) +{ + return container_of(gem_obj, struct ingenic_gem_object, base.base); +} + static inline dma_addr_t dma_hwdesc_addr(const struct ingenic_drm *priv, unsigned int idx) { @@ -481,7 +509,7 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, return PTR_ERR(priv_state); ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, - DRM_PLANE_NO_SCALING, + 0x8000, DRM_PLANE_NO_SCALING, priv->soc_info->has_osd, true); @@ -498,6 +526,17 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)) return -EINVAL; + /* Enable doublescan if the CRTC_H is twice the SRC_H. */ + priv_state->doublescan = (new_plane_state->src_h >> 16) * 2 == new_plane_state->crtc_h; + + /* Otherwise, fail if CRTC_H != SRC_H */ + if (!priv_state->doublescan && (new_plane_state->src_h >> 16) != new_plane_state->crtc_h) + return -EINVAL; + + /* Fail if CRTC_W != SRC_W */ + if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w) + return -EINVAL; + priv_state->use_palette = new_plane_state->fb && new_plane_state->fb->format->format == DRM_FORMAT_C8; @@ -549,6 +588,7 @@ void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane) regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); } } +EXPORT_SYMBOL_GPL(ingenic_drm_plane_disable); static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) @@ -632,6 +672,7 @@ void ingenic_drm_plane_config(struct device *dev, state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); } } +EXPORT_SYMBOL_GPL(ingenic_drm_plane_config); bool ingenic_drm_map_noncoherent(const struct device *dev) { @@ -639,6 +680,7 @@ bool ingenic_drm_map_noncoherent(const struct device *dev) return priv->soc_info->map_noncoherent; } +EXPORT_SYMBOL_GPL(ingenic_drm_map_noncoherent); static void ingenic_drm_update_palette(struct ingenic_drm *priv, const struct drm_color_lut *lut) @@ -664,7 +706,10 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, struct ingenic_drm_private_state *priv_state; struct drm_crtc_state *crtc_state; struct ingenic_dma_hwdesc *hwdesc; + struct drm_gem_object *gem_obj; + struct ingenic_gem_object *obj; dma_addr_t addr; + unsigned int i; u32 fourcc; if (newstate && newstate->fb) { @@ -679,13 +724,36 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, height = newstate->src_h >> 16; cpp = newstate->fb->format->cpp[0]; + gem_obj = drm_gem_fb_get_obj(newstate->fb, 0); + obj = to_ingenic_gem_obj(gem_obj); + priv_state = ingenic_drm_get_new_priv_state(priv, state); next_id = (priv_state && priv_state->use_palette) ? HWDESC_PALETTE : plane_id; - hwdesc = &priv->dma_hwdescs->hwdesc[plane_id]; - hwdesc->addr = addr; - hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); - hwdesc->next = dma_hwdesc_addr(priv, next_id); + if (priv_state->doublescan) { + hwdesc = &obj->hwdescs[0]; + /* + * Use one DMA descriptor per output line, and display + * each input line twice. + */ + for (i = 0; i < newstate->crtc_h; i++) { + hwdesc[i].next = obj->hwdescs_phys + + (i + 1) * sizeof(*hwdesc); + hwdesc[i].addr = addr + (i / 2) * newstate->fb->pitches[0]; + hwdesc[i].cmd = newstate->fb->pitches[0] / 4; + } + + /* We want the EOF IRQ only on the very last transfer */ + hwdesc[newstate->crtc_h - 1].cmd |= JZ_LCD_CMD_EOF_IRQ; + hwdesc[newstate->crtc_h - 1].next = dma_hwdesc_addr(priv, next_id); + priv->dma_hwdescs->hwdesc[plane_id] = *hwdesc; + } else { + /* Use one DMA descriptor for the whole frame. */ + hwdesc = &priv->dma_hwdescs->hwdesc[plane_id]; + hwdesc->addr = addr; + hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); + hwdesc->next = dma_hwdesc_addr(priv, next_id); + } if (priv->soc_info->use_extended_hwdesc) { hwdesc->cmd |= JZ_LCD_CMD_FRM_ENABLE; @@ -898,31 +966,85 @@ static void ingenic_drm_disable_vblank(struct drm_crtc *crtc) regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, 0); } +static void ingenic_drm_gem_fb_destroy(struct drm_framebuffer *fb) +{ + struct ingenic_drm *priv = drm_device_get_priv(fb->dev); + struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); + struct ingenic_gem_object *obj = to_ingenic_gem_obj(gem_obj); + + dma_free_coherent(priv->dev, + sizeof(*obj->hwdescs) * fb->height * 2, + obj->hwdescs, obj->hwdescs_phys); + drm_gem_fb_destroy(fb); +} + +static int ingenic_drm_gem_dirtyfb(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int flags, + unsigned int color, struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct ingenic_drm *priv = drm_device_get_priv(fb->dev); + + if (priv->soc_info->map_noncoherent) { + return drm_atomic_helper_dirtyfb(fb, file_priv, flags, color, + clips, num_clips); + } + + return 0; +} + +static const struct drm_framebuffer_funcs ingenic_drm_gem_fb_funcs = { + .destroy = ingenic_drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, + .dirty = ingenic_drm_gem_dirtyfb, +}; + static struct drm_framebuffer * -ingenic_drm_gem_fb_create(struct drm_device *drm, struct drm_file *file, +ingenic_drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) { - struct ingenic_drm *priv = drm_device_get_priv(drm); + struct ingenic_drm *priv = drm_device_get_priv(dev); + struct drm_gem_object *gem_obj; + struct ingenic_gem_object *obj; + struct drm_framebuffer *fb; - if (priv->soc_info->map_noncoherent) - return drm_gem_fb_create_with_dirty(drm, file, mode_cmd); + fb = drm_gem_fb_create_with_funcs(dev, file, mode_cmd, + &ingenic_drm_gem_fb_funcs); + if (IS_ERR(fb)) + return fb; + + gem_obj = drm_gem_fb_get_obj(fb, 0); + obj = to_ingenic_gem_obj(gem_obj); + + /* + * Create (fb->height * 2) DMA descriptors, in case we want to use the + * doublescan feature. + */ + obj->hwdescs = dma_alloc_coherent(priv->dev, + sizeof(*obj->hwdescs) * fb->height * 2, + &obj->hwdescs_phys, + GFP_KERNEL); + if (!obj->hwdescs) { + drm_gem_fb_destroy(fb); + return ERR_PTR(-ENOMEM); + } - return drm_gem_fb_create(drm, file, mode_cmd); + return fb; } static struct drm_gem_object * ingenic_drm_gem_create_object(struct drm_device *drm, size_t size) { struct ingenic_drm *priv = drm_device_get_priv(drm); - struct drm_gem_dma_object *obj; + struct ingenic_gem_object *obj; obj = kzalloc(sizeof(*obj), GFP_KERNEL); if (!obj) return ERR_PTR(-ENOMEM); - obj->map_noncoherent = priv->soc_info->map_noncoherent; + obj->base.map_noncoherent = priv->soc_info->map_noncoherent; - return &obj->base; + return &obj->base.base; } static struct drm_private_state * @@ -1078,6 +1200,17 @@ static void ingenic_drm_atomic_private_obj_fini(struct drm_device *drm, void *pr drm_atomic_private_obj_fini(private_obj); } +static struct clk * ingenic_drm_get_parent_clk(struct clk *clk) +{ + /* + * Return the first clock above the one that will effectively modify + * its rate when clk_set_rate(clk) is called. + */ + clk = clk_get_first_to_set_rate(clk); + + return clk_get_parent(clk); +} + static int ingenic_drm_bind(struct device *dev, bool has_components) { struct platform_device *pdev = to_platform_device(dev); @@ -1087,7 +1220,6 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) struct clk *parent_clk; struct drm_plane *primary; struct drm_bridge *bridge; - struct drm_panel *panel; struct drm_connector *connector; struct drm_encoder *encoder; struct ingenic_drm_bridge *ib; @@ -1259,8 +1391,9 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) } for (i = 0; ; i++) { - ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i, &panel, &bridge); - if (ret) { + bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, i); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); if (ret == -ENODEV) break; /* we're done */ if (ret != -EPROBE_DEFER) @@ -1268,10 +1401,6 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) return ret; } - if (panel) - bridge = devm_drm_panel_bridge_add_typed(dev, panel, - DRM_MODE_CONNECTOR_DPI); - ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder, NULL, DRM_MODE_ENCODER_DPI, NULL); if (IS_ERR(ib)) { @@ -1364,7 +1493,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) mutex_init(&priv->clk_mutex); priv->clock_nb.notifier_call = ingenic_drm_update_pixclk; - parent_clk = clk_get_parent(priv->pix_clk); + parent_clk = ingenic_drm_get_parent_clk(priv->pix_clk); + ret = clk_notifier_register(parent_clk, &priv->clock_nb); if (ret) { dev_err(dev, "Unable to register clock notifier\n"); @@ -1415,7 +1545,7 @@ static int ingenic_drm_bind_with_components(struct device *dev) static void ingenic_drm_unbind(struct device *dev) { struct ingenic_drm *priv = dev_get_drvdata(dev); - struct clk *parent_clk = clk_get_parent(priv->pix_clk); + struct clk *parent_clk = ingenic_drm_get_parent_clk(priv->pix_clk); clk_notifier_unregister(parent_clk, &priv->clock_nb); if (priv->lcd_clk) @@ -1437,6 +1567,9 @@ static int ingenic_drm_probe(struct platform_device *pdev) struct component_match *match = NULL; struct device_node *np; + if (drm_firmware_drivers_only()) + return -ENODEV; + if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) return ingenic_drm_bind(dev, false); @@ -1615,32 +1748,7 @@ static struct platform_driver ingenic_drm_driver = { .probe = ingenic_drm_probe, .remove = ingenic_drm_remove, }; - -static int ingenic_drm_init(void) -{ - int err; - - if (drm_firmware_drivers_only()) - return -ENODEV; - - if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { - err = platform_driver_register(ingenic_ipu_driver_ptr); - if (err) - return err; - } - - return platform_driver_register(&ingenic_drm_driver); -} -module_init(ingenic_drm_init); - -static void ingenic_drm_exit(void) -{ - platform_driver_unregister(&ingenic_drm_driver); - - if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) - platform_driver_unregister(ingenic_ipu_driver_ptr); -} -module_exit(ingenic_drm_exit); +module_platform_driver(ingenic_drm_driver); MODULE_AUTHOR("Paul Cercueil "); MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n"); diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h index e5bd007ea93d8d..9b89929b81bc77 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.h +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h @@ -220,13 +220,10 @@ struct device; struct drm_plane; struct drm_plane_state; -struct platform_driver; void ingenic_drm_plane_config(struct device *dev, struct drm_plane *plane, u32 fourcc); void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane); bool ingenic_drm_map_noncoherent(const struct device *dev); -extern struct platform_driver *ingenic_ipu_driver_ptr; - #endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */ diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 7a43505011a571..9eb8a2abbabc96 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -994,5 +995,8 @@ static struct platform_driver ingenic_ipu_driver = { .probe = ingenic_ipu_probe, .remove = ingenic_ipu_remove, }; +module_platform_driver(ingenic_ipu_driver); -struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver; +MODULE_AUTHOR("Paul Cercueil "); +MODULE_DESCRIPTION("DRM driver for the IPU of Ingenic SoCs\n"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a582ddd583c242..cd14c22e625e46 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -8,6 +8,14 @@ config DRM_PANEL menu "Display Panels" depends on DRM && DRM_PANEL +config DRM_PANEL_AUO_A030JTN01 + tristate "AUO A030JTN01" + depends on OF && SPI + select REGMAP_SPI + help + Say Y here to enable support for the AUO A030JTN01 320x480 3.0" panel + as found in the YLM RS-97 handheld gaming console. + config DRM_PANEL_ABT_Y030XX067A tristate "ABT Y030XX067A 320x480 LCD panel" depends on OF && SPI @@ -381,6 +389,15 @@ config DRM_PANEL_OLIMEX_LCD_OLINUXINO Say Y here if you want to enable support for Olimex Ltd. LCD-OLinuXino panel. +config DRM_PANEL_ORISETECH_OTA5601A + tristate "Orise Technology ota5601a RGB/SPI panel" + depends on OF && SPI + depends on BACKLIGHT_CLASS_DEVICE + select REGMAP_SPI + help + Say Y here if you want to enable support for the panels built + around the Orise Technology OTA9601A display controller. + config DRM_PANEL_ORISETECH_OTM8009A tristate "Orise Technology otm8009a 480x800 dsi 2dl panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 34e717382dbb65..552ce43f29539b 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o @@ -35,6 +36,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o diff --git a/drivers/gpu/drm/panel/panel-auo-a030jtn01.c b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c new file mode 100644 index 00000000000000..1c4f812e948316 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AU Optronics A030JTN01.0 TFT LCD panel driver + * + * Copyright (C) 2020, Paul Cercueil + * Copyright (C) 2020, Christophe Branchereau + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct a030jtn01_info { + const struct drm_display_mode *display_modes; + unsigned int num_modes; + u16 width_mm, height_mm; + u32 bus_format, bus_flags; +}; + +struct a030jtn01 { + struct drm_panel panel; + struct spi_device *spi; + struct regmap *map; + + const struct a030jtn01_info *panel_info; + + struct regulator *supply; + struct gpio_desc *reset_gpio; +}; + +static inline struct a030jtn01 *to_a030jtn01(struct drm_panel *panel) +{ + return container_of(panel, struct a030jtn01, panel); +} + +static int a030jtn01_prepare(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + struct device *dev = &priv->spi->dev; + int err; + + err = regulator_enable(priv->supply); + if (err) { + dev_err(dev, "Failed to enable power supply: %d\n", err); + return err; + } + + usleep_range(1000, 8000); + + /* Reset the chip */ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(100, 8000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(2000, 8000); + + /* + * No idea why two writes are needed. If this write is commented, + * the colors are wrong. Doesn't seem to be timing-related, since + * a msleep(200) doesn't fix it. + */ + regmap_write(priv->map, 0x06, 0x00); + + /* Use (24 + 6) == 0x1e as the vertical back porch */ + err = regmap_write(priv->map, 0x06, 0x1e); + if (err) + goto err_disable_regulator; + + /* Use (42 + 30) * 3 == 0xd8 as the horizontal back porch */ + err = regmap_write(priv->map, 0x07, 0xd8); + if (err) + goto err_disable_regulator; + + regmap_write(priv->map, 0x05, 0x74); + + return 0; + +err_disable_regulator: + gpiod_set_value_cansleep(priv->reset_gpio, 1); + regulator_disable(priv->supply); + return err; +} + +static int a030jtn01_unprepare(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + + gpiod_set_value_cansleep(priv->reset_gpio, 1); + regulator_disable(priv->supply); + + return 0; +} + +static int a030jtn01_enable(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + int ret; + + ret = regmap_write(priv->map, 0x05, 0x75); + if (ret) + return ret; + + /* Wait for the picture to be stable */ + if (panel->backlight) + msleep(100); + + return 0; +} + +static int a030jtn01_disable(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + + return regmap_write(priv->map, 0x05, 0x74); +} + +static int a030jtn01_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + const struct a030jtn01_info *panel_info = priv->panel_info; + struct drm_display_mode *mode; + unsigned int i; + + for (i = 0; i < panel_info->num_modes; i++) { + mode = drm_mode_duplicate(connector->dev, + &panel_info->display_modes[i]); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + if (panel_info->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + + drm_display_info_set_bus_formats(&connector->display_info, + &panel_info->bus_format, 1); + connector->display_info.bus_flags = panel_info->bus_flags; + + return panel_info->num_modes; +} + +static const struct drm_panel_funcs a030jtn01_funcs = { + .prepare = a030jtn01_prepare, + .unprepare = a030jtn01_unprepare, + .enable = a030jtn01_enable, + .disable = a030jtn01_disable, + .get_modes = a030jtn01_get_modes, +}; + +static bool a030jtn01_has_reg(struct device *dev, unsigned int reg) +{ + static const u32 a030jtn01_regs_mask = 0x001823f1fb; + + return a030jtn01_regs_mask & BIT(reg); +}; + +static const struct regmap_config a030jtn01_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x40, + .max_register = 0x1c, + .readable_reg = a030jtn01_has_reg, + .writeable_reg = a030jtn01_has_reg, +}; + +static int a030jtn01_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct a030jtn01 *priv; + int err; + + spi->mode |= SPI_MODE_3 | SPI_3WIRE; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spi = spi; + spi_set_drvdata(spi, priv); + + priv->map = devm_regmap_init_spi(spi, &a030jtn01_regmap_config); + if (IS_ERR(priv->map)) { + dev_err(dev, "Unable to init regmap\n"); + return PTR_ERR(priv->map); + } + + priv->panel_info = of_device_get_match_data(dev); + if (!priv->panel_info) + return -EINVAL; + + priv->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(priv->supply)) { + dev_err(dev, "Failed to get power supply\n"); + return PTR_ERR(priv->supply); + } + + priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "Failed to get reset GPIO\n"); + return PTR_ERR(priv->reset_gpio); + } + + drm_panel_init(&priv->panel, dev, &a030jtn01_funcs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&priv->panel); + if (err) + return err; + + drm_panel_add(&priv->panel); + + return 0; +} + +static void a030jtn01_remove(struct spi_device *spi) +{ + struct a030jtn01 *priv = spi_get_drvdata(spi); + + drm_panel_remove(&priv->panel); + drm_panel_disable(&priv->panel); + drm_panel_unprepare(&priv->panel); +} + +static const struct drm_display_mode a030jtn01_modes[] = { + { /* 60 Hz */ + .clock = 14400, + .hdisplay = 320, + .hsync_start = 320 + 8, + .hsync_end = 320 + 8 + 42, + .htotal = 320 + 8 + 42 + 30, + .vdisplay = 480, + .vsync_start = 480 + 90, + .vsync_end = 480 + 90 + 24, + .vtotal = 480 + 90 + 24 + 6, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + { /* 50 Hz */ + .clock = 12000, + .hdisplay = 320, + .hsync_start = 320 + 8, + .hsync_end = 320 + 8 + 42, + .htotal = 320 + 8 + 42 + 30, + .vdisplay = 480, + .vsync_start = 480 + 90, + .vsync_end = 480 + 90 + 24, + .vtotal = 480 + 90 + 24 + 6, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct a030jtn01_info a030jtn01_info = { + .display_modes = a030jtn01_modes, + .num_modes = ARRAY_SIZE(a030jtn01_modes), + .width_mm = 70, + .height_mm = 51, + .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, + .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, +}; + +static const struct of_device_id a030jtn01_of_match[] = { + { .compatible = "auo,a030jtn01", .data = &a030jtn01_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, a030jtn01_of_match); + +static struct spi_driver a030jtn01_driver = { + .driver = { + .name = "auo-a030jtn01", + .of_match_table = a030jtn01_of_match, + }, + .probe = a030jtn01_probe, + .remove = a030jtn01_remove, +}; +module_spi_driver(a030jtn01_driver); + +MODULE_AUTHOR("Paul Cercueil "); +MODULE_AUTHOR("Christophe Branchereau "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c new file mode 100644 index 00000000000000..ea6d452c61f0f8 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Orisetech OTA5601A TFT LCD panel driver + * + * Copyright (C) 2021, Christophe Branchereau + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ota5601a_panel_info { + const struct drm_display_mode *display_modes; + unsigned int num_modes; + u16 width_mm, height_mm; + u32 bus_format, bus_flags; +}; + +struct ota5601a { + struct drm_panel drm_panel; + struct regmap *map; + struct regulator *supply; + const struct ota5601a_panel_info *panel_info; + + struct gpio_desc *reset_gpio; +}; + +static inline struct ota5601a *to_ota5601a(struct drm_panel *panel) +{ + return container_of(panel, struct ota5601a, drm_panel); +} + +static const struct reg_sequence ota5601a_panel_regs[] = { + { 0xfd, 0x00 }, + { 0x02, 0x00 }, + + { 0x18, 0x00 }, + { 0x34, 0x20 }, + + { 0x0c, 0x01 }, + { 0x0d, 0x48 }, + { 0x0e, 0x48 }, + { 0x0f, 0x48 }, + { 0x07, 0x40 }, + { 0x08, 0x33 }, + { 0x09, 0x3a }, + + { 0x16, 0x01 }, + { 0x19, 0x8d }, + { 0x1a, 0x28 }, + { 0x1c, 0x00 }, + + { 0xfd, 0xc5 }, + { 0x82, 0x0c }, + { 0xa2, 0xb4 }, + + { 0xfd, 0xc4 }, + { 0x82, 0x45 }, + + { 0xfd, 0xc1 }, + { 0x91, 0x02 }, + + { 0xfd, 0xc0 }, + { 0xa1, 0x01 }, + { 0xa2, 0x1f }, + { 0xa3, 0x0b }, + { 0xa4, 0x38 }, + { 0xa5, 0x00 }, + { 0xa6, 0x0a }, + { 0xa7, 0x38 }, + { 0xa8, 0x00 }, + { 0xa9, 0x0a }, + { 0xaa, 0x37 }, + + { 0xfd, 0xce }, + { 0x81, 0x18 }, + { 0x82, 0x43 }, + { 0x83, 0x43 }, + { 0x91, 0x06 }, + { 0x93, 0x38 }, + { 0x94, 0x02 }, + { 0x95, 0x06 }, + { 0x97, 0x38 }, + { 0x98, 0x02 }, + { 0x99, 0x06 }, + { 0x9b, 0x38 }, + { 0x9c, 0x02 }, + + { 0xfd, 0x00 }, +}; + +static const struct regmap_config ota5601a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ota5601a_prepare(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regulator_enable(panel->supply); + if (err) { + dev_err(drm_panel->dev, "Failed to enable power supply: %d\n", err); + return err; + } + + /* Reset should be held low for 10us min according to the doc, 10ms before sending commands */ + gpiod_set_value_cansleep(panel->reset_gpio, 1); + usleep_range(10, 30); + gpiod_set_value_cansleep(panel->reset_gpio, 0); + msleep(10); + + /* Init all registers. */ + err = regmap_multi_reg_write(panel->map, ota5601a_panel_regs, + ARRAY_SIZE(ota5601a_panel_regs)); + if (err) { + dev_err(drm_panel->dev, "Failed to init registers: %d\n", err); + goto err_disable_regulator; + } + + msleep(120); + + return 0; + +err_disable_regulator: + regulator_disable(panel->supply); + return err; +} + +static int ota5601a_unprepare(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + + gpiod_set_value_cansleep(panel->reset_gpio, 1); + + regulator_disable(panel->supply); + + return 0; +} + +static int ota5601a_enable(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regmap_write(panel->map, 0x01, 0x01); + + if (err) { + dev_err(drm_panel->dev, "Unable to enable panel: %d\n", err); + return err; + } + + if (drm_panel->backlight) { + /* Wait for the picture to be ready before enabling backlight */ + msleep(120); + } + + return 0; +} + +static int ota5601a_disable(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regmap_write(panel->map, 0x01, 0x00); + + if (err) { + dev_err(drm_panel->dev, "Unable to disable panel: %d\n", err); + return err; + } + + return 0; +} + +static int ota5601a_get_modes(struct drm_panel *drm_panel, + struct drm_connector *connector) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + const struct ota5601a_panel_info *panel_info = panel->panel_info; + struct drm_display_mode *mode; + unsigned int i; + + for (i = 0; i < panel_info->num_modes; i++) { + mode = drm_mode_duplicate(connector->dev, + &panel_info->display_modes[i]); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + if (panel_info->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + + drm_display_info_set_bus_formats(&connector->display_info, + &panel_info->bus_format, 1); + connector->display_info.bus_flags = panel_info->bus_flags; + + return panel_info->num_modes; +} + +static const struct drm_panel_funcs ota5601a_funcs = { + .prepare = ota5601a_prepare, + .unprepare = ota5601a_unprepare, + .enable = ota5601a_enable, + .disable = ota5601a_disable, + .get_modes = ota5601a_get_modes, +}; + +static int ota5601a_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ota5601a *panel; + int err; + + panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); + if (!panel) + return -ENOMEM; + + spi_set_drvdata(spi, panel); + + panel->panel_info = of_device_get_match_data(dev); + if (!panel->panel_info) + return -EINVAL; + + panel->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(panel->supply)) { + dev_err(dev, "Failed to get power supply\n"); + return PTR_ERR(panel->supply); + } + + panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(panel->reset_gpio)) { + dev_err(dev, "Failed to get reset GPIO\n"); + return PTR_ERR(panel->reset_gpio); + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3 | SPI_3WIRE; + err = spi_setup(spi); + if (err) { + dev_err(dev, "Failed to setup SPI\n"); + return err; + } + + panel->map = devm_regmap_init_spi(spi, &ota5601a_regmap_config); + if (IS_ERR(panel->map)) { + dev_err(dev, "Failed to init regmap\n"); + return PTR_ERR(panel->map); + } + + drm_panel_init(&panel->drm_panel, dev, &ota5601a_funcs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&panel->drm_panel); + if (err) { + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get backlight handle\n"); + return err; + } + + drm_panel_add(&panel->drm_panel); + + return 0; +} + +static void ota5601a_remove(struct spi_device *spi) +{ + struct ota5601a *panel = spi_get_drvdata(spi); + + drm_panel_remove(&panel->drm_panel); + + ota5601a_disable(&panel->drm_panel); + ota5601a_unprepare(&panel->drm_panel); +} + +static const struct drm_display_mode gpt3_display_modes[] = { + { /* 60 Hz */ + .clock = 27000, + .hdisplay = 640, + .hsync_start = 640 + 220, + .hsync_end = 640 + 220 + 20, + .htotal = 640 + 220 + 20 + 20, + .vdisplay = 480, + .vsync_start = 480 + 7, + .vsync_end = 480 + 7 + 6, + .vtotal = 480 + 7 + 6 + 7, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + + { /* 50 Hz */ + .clock = 24000, + .hdisplay = 640, + .hsync_start = 640 + 280, + .hsync_end = 640 + 280 + 20, + .htotal = 640 + 280 + 20 + 20, + .vdisplay = 480, + .vsync_start = 480 + 7, + .vsync_end = 480 + 7 + 6, + .vtotal = 480 + 7 + 6 + 7, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct ota5601a_panel_info gpt3_info = { + .display_modes = gpt3_display_modes, + .num_modes = ARRAY_SIZE(gpt3_display_modes), + .width_mm = 71, + .height_mm = 51, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, +}; + +static const struct of_device_id ota5601a_of_match[] = { + { .compatible = "focaltech,gpt3", .data = &gpt3_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ota5601a_of_match); + +static struct spi_driver ota5601a_driver = { + .driver = { + .name = "ota5601a", + .of_match_table = ota5601a_of_match, + }, + .probe = ota5601a_probe, + .remove = ota5601a_remove, +}; + +module_spi_driver(ota5601a_driver); + +MODULE_AUTHOR("Christophe Branchereau "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index 3874d15b0e9b71..7c6b3e1ef780f9 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -8,6 +8,14 @@ menuconfig HWSPINLOCK if HWSPINLOCK +config HWSPINLOCK_INGENIC + tristate "Ingenic VPU Hardware Spinlock device" + depends on MACH_INGENIC || COMPILE_TEST + help + Say y here to support the Ingenic VPU Hardware Spinlock device. + + If unsure, say N. + config HWSPINLOCK_OMAP tristate "OMAP Hardware Spinlock device" depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX || SOC_AM33XX || SOC_AM43XX || ARCH_K3 || COMPILE_TEST diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index a0f16c9aaa8212..cb6bf09075177b 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o +obj-$(CONFIG_HWSPINLOCK_INGENIC) += ingenic_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o obj-$(CONFIG_HWSPINLOCK_SPRD) += sprd_hwspinlock.o diff --git a/drivers/hwspinlock/ingenic_hwspinlock.c b/drivers/hwspinlock/ingenic_hwspinlock.c new file mode 100644 index 00000000000000..00abc6f3c6643a --- /dev/null +++ b/drivers/hwspinlock/ingenic_hwspinlock.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic VPU hardware spinlock driver +// +// Copyright (C) 2020, Paul Cercueil + +#include +#include +#include +#include +#include +#include +#include + +#include "hwspinlock_internal.h" + +#define REG_AUX_SPINLK 0x0 +#define REG_AUX_SPIN1 0x4 +#define REG_AUX_SPIN2 0x8 + +#define AUX_SPIN1_LOCKED BIT(0) +#define AUX_SPIN2_LOCKED BIT(1) + +struct ingenic_lock { + void __iomem *base; + struct clk *clk; + struct hwspinlock_device bank; +}; + +static int ingenic_hwspinlock_trylock(struct hwspinlock *lock) +{ + struct ingenic_lock *priv = lock->priv; + int err; + u32 val; + + err = clk_enable(priv->clk); + if (err) + return err; + + readl(priv->base + REG_AUX_SPIN1); + + val = readl(priv->base + REG_AUX_SPINLK); + + return val == AUX_SPIN1_LOCKED; +} + +static void ingenic_hwspinlock_unlock(struct hwspinlock *lock) +{ + struct ingenic_lock *priv = lock->priv; + + writel(0, priv->base + REG_AUX_SPINLK); + clk_disable(priv->clk); +} + +static const struct hwspinlock_ops ingenic_hwspinlock_ops = { + .trylock = ingenic_hwspinlock_trylock, + .unlock = ingenic_hwspinlock_unlock, +}; + +static void ingenic_hwspinlock_clk_unprepare(void *d) +{ + clk_unprepare(d); +} + +static int ingenic_hwspinlock_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ingenic_lock *priv; + int err; + + priv = devm_kzalloc(dev, struct_size(priv, bank.lock, 1), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(dev->parent, "aux"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n"); + + err = clk_prepare(priv->clk); + if (err) + return err; + + err = devm_add_action_or_reset(dev, ingenic_hwspinlock_clk_unprepare, + priv->clk); + if (err) + return err; + + priv->bank.lock->priv = priv; + + /* Init registers */ + writel(0, priv->base + REG_AUX_SPINLK); + writel(AUX_SPIN1_LOCKED, priv->base + REG_AUX_SPIN1); + writel(AUX_SPIN2_LOCKED, priv->base + REG_AUX_SPIN2); + + return devm_hwspin_lock_register(dev, &priv->bank, + &ingenic_hwspinlock_ops, 0, 1); +} + +static const struct of_device_id ingenic_hwspinlock_of_match[] = { + { .compatible = "ingenic,jz4760-vpu-hwspinlock", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ingenic_hwspinlock_of_match); + +static struct platform_driver ingenic_hwspinlock_driver = { + .probe = ingenic_hwspinlock_probe, + .driver = { + .name = "ingenic-hwspinlock", + .of_match_table = ingenic_hwspinlock_of_match, + }, +}; +module_platform_driver(ingenic_hwspinlock_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Ingenic VPU hardware spinlock driver"); +MODULE_AUTHOR("Paul Cercueil "); diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 7539b0740351d1..13fafb74bab8d9 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -2237,6 +2237,20 @@ int i2c_get_device_id(const struct i2c_client *client, } EXPORT_SYMBOL_GPL(i2c_get_device_id); +/** + * i2c_client_get_device_id - get the driver match table entry of a device + * @client: the device to query. The device must be bound to a driver + * + * Returns a pointer to the matching entry if found, NULL otherwise. + */ +const struct i2c_device_id *i2c_client_get_device_id(const struct i2c_client *client) +{ + const struct i2c_driver *drv = to_i2c_driver(client->dev.driver); + + return i2c_match_id(drv->id_table, client); +} +EXPORT_SYMBOL_GPL(i2c_client_get_device_id); + /* ---------------------------------------------------- * the i2c address scanning function * Will not work for 10-bit addresses! diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index a7325dbbb99a04..5a932c375a8969 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -804,11 +804,10 @@ static irqreturn_t ingenic_adc_irq(int irq, void *data) unsigned int i; u32 tdat[3]; - for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) { + memset(tdat, 0, ARRAY_SIZE(tdat)); + for (i = 0; mask && i < ARRAY_SIZE(tdat); mask >>= 2) { if (mask & 0x3) - tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH); - else - tdat[i] = 0; + tdat[i++] = readl(adc->base + JZ_ADC_REG_ADTCH); } iio_push_to_buffers(iio_dev, tdat); diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 1f280c360701bc..f793c9866b457a 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -143,6 +143,27 @@ int rescale_process_offset(struct rescale *rescale, int scale_type, } EXPORT_SYMBOL_NS_GPL(rescale_process_offset, IIO_RESCALE); +static int rescale_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct rescale *rescale = iio_priv(indio_dev); + unsigned long long tmp; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + tmp = val * 1000000000LL; + do_div(tmp, rescale->numerator); + tmp *= rescale->denominator; + do_div(tmp, 1000000000LL); + return iio_write_channel_attribute(rescale->source, tmp, 0, + IIO_CHAN_INFO_SCALE); + default: + return iio_write_channel_attribute(rescale->source, + val, val2, mask); + } +} + static int rescale_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -234,12 +255,25 @@ static int rescale_read_avail(struct iio_dev *indio_dev, *type = IIO_VAL_INT; return iio_read_avail_channel_raw(rescale->source, vals, length); + case IIO_CHAN_INFO_SCALE: + if (rescale->chan_processed) { + return iio_read_avail_channel_attribute(rescale->source, + vals, type, + length, + IIO_CHAN_INFO_SCALE); + } else if (rescale->scale_len) { + *length = rescale->scale_len; + *vals = rescale->scale_data; + return IIO_AVAIL_LIST_WITH_TYPE; + } + fallthrough; default: return -EINVAL; } } static const struct iio_info rescale_info = { + .write_raw = rescale_write_raw, .read_raw = rescale_read_raw, .read_avail = rescale_read_avail, }; @@ -268,11 +302,74 @@ static ssize_t rescale_write_ext_info(struct iio_dev *indio_dev, buf, len); } +static int rescale_init_scale_avail(struct device *dev, struct rescale *rescale) +{ + int ret, type, length, *data; + const int *scale_raw; + unsigned int i; + size_t out_len; + + ret = iio_read_avail_channel_attribute(rescale->source, &scale_raw, + &type, &length, + IIO_CHAN_INFO_SCALE); + if (ret < 0) + return ret; + + switch (ret) { + case IIO_AVAIL_LIST_WITH_TYPE: + out_len = length; + break; + case IIO_AVAIL_LIST: + if (type == IIO_VAL_INT) + out_len = length * 3 / 1; + else + out_len = length * 3 / 2; + break; + default: + /* TODO: Support IIO_AVAIL_RANGE */ + return -ENOTSUPP; + } + + data = devm_kzalloc(dev, sizeof(*data) * out_len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (ret == IIO_AVAIL_LIST_WITH_TYPE) { + memcpy(data, scale_raw, sizeof(*scale_raw) * length); + } else if (type == IIO_VAL_INT) { + for (i = 0; i < length; i++) { + data[i * 3 + 0] = scale_raw[i]; + data[i * 3 + 2] = IIO_VAL_INT; + } + } else { + for (i = 0; i < length / 2; i++) { + data[i * 3 + 0] = scale_raw[i * 2]; + data[i * 3 + 1] = scale_raw[i * 2 + 1]; + data[i * 3 + 2] = type; + } + } + + for (i = 0; i < out_len; i += 3) { + ret = rescale_process_scale(rescale, data[i + 2], + &data[i], &data[i + 1]); + if (ret < 0) + return ret; + + data[i + 2] = ret; + } + + rescale->scale_len = out_len; + rescale->scale_data = data; + + return 0; +} + static int rescale_configure_channel(struct device *dev, struct rescale *rescale) { struct iio_chan_spec *chan = &rescale->chan; struct iio_chan_spec const *schan = rescale->source->channel; + int ret; chan->indexed = 1; chan->output = schan->output; @@ -305,6 +402,16 @@ static int rescale_configure_channel(struct device *dev, !rescale->chan_processed) chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); + if (iio_channel_has_available(schan, IIO_CHAN_INFO_SCALE)) { + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_SCALE); + + if (!rescale->chan_processed) { + ret = rescale_init_scale_avail(dev, rescale); + if (ret) + return ret; + } + } + return 0; } diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 4c12b7a94af59a..47d6e28b4d36f8 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -151,6 +151,13 @@ struct iio_dev } EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); +struct iio_buffer +*iio_channel_cb_get_iio_buffer(struct iio_cb_buffer *cb_buffer) +{ + return &cb_buffer->buffer; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_buffer); + MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 228598b82a2f36..cf23736610d9ab 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -691,6 +691,34 @@ static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, return bytes; } +int iio_find_channel_offset_in_buffer(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + struct iio_buffer *buffer) +{ + int length, offset = 0; + unsigned int si; + + if (chan->scan_index < 0 || + !test_bit(chan->scan_index, buffer->scan_mask)) { + return -EINVAL; + } + + for (si = 0; si < chan->scan_index; ++si) { + if (!test_bit(si, buffer->scan_mask)) + continue; + + length = iio_storage_bytes_for_si(indio_dev, si); + + /* Account for channel alignment. */ + if (offset % length) + offset += length - (offset % length); + offset += length; + } + + return offset; +} +EXPORT_SYMBOL_GPL(iio_find_channel_offset_in_buffer); + static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 151ff39933548c..1f807bf006af2a 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -840,6 +840,29 @@ static ssize_t iio_format_avail_range(char *buf, const int *vals, int type) return iio_format_list(buf, vals, type, length, "[", "]"); } +static ssize_t iio_format_avail_list_with_type(char *buf, const int *vals, + int length) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < length; i += 3) { + if (i != 0) { + len += sysfs_emit_at(buf, len, " "); + if (len >= PAGE_SIZE) + return -EFBIG; + } + + len += __iio_format_value(buf, len, vals[i + 2], 2, &vals[i]); + if (len >= PAGE_SIZE) + return -EFBIG; + } + + len += sysfs_emit_at(buf, len, "\n"); + + return len; +} + static ssize_t iio_read_channel_info_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -862,6 +885,8 @@ static ssize_t iio_read_channel_info_avail(struct device *dev, return iio_format_avail_list(buf, vals, type, length); case IIO_AVAIL_RANGE: return iio_format_avail_range(buf, vals, type); + case IIO_AVAIL_LIST_WITH_TYPE: + return iio_format_avail_list_with_type(buf, vals, length); default: return -EINVAL; } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 872fd5c241476e..6614638aa81b37 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -846,21 +846,22 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); static int iio_channel_read_max(struct iio_channel *chan, - int *val, int *val2, int *type, + int *val, int *val2, enum iio_chan_info_enum info) { int unused; const int *vals; int length; int ret; + int type; if (!val2) val2 = &unused; - ret = iio_channel_read_avail(chan, &vals, type, &length, info); + ret = iio_channel_read_avail(chan, &vals, &type, &length, info); switch (ret) { case IIO_AVAIL_RANGE: - switch (*type) { + switch (type) { case IIO_VAL_INT: *val = vals[2]; break; @@ -873,7 +874,7 @@ static int iio_channel_read_max(struct iio_channel *chan, case IIO_AVAIL_LIST: if (length <= 0) return -EINVAL; - switch (*type) { + switch (type) { case IIO_VAL_INT: *val = vals[--length]; while (length) { @@ -887,6 +888,29 @@ static int iio_channel_read_max(struct iio_channel *chan, } return 0; + case IIO_AVAIL_LIST_WITH_TYPE: + if (length <= 0 || length % 3 != 0) + return -EINVAL; + + if (vals[length - 1] != IIO_VAL_INT) { + /* FIXME: learn about max for other iio values */ + return -EINVAL; + } + + *val = vals[length - 3]; + length -= 3; + + for (; length; length -= 3) { + if (vals[length - 1] != IIO_VAL_INT) { + /* FIXME: learn about max for other iio values */ + return -EINVAL; + } + + if (vals[length - 3] > *val) + *val = vals[length - 3]; + } + return 0; + default: return ret; } @@ -896,7 +920,6 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - int type; mutex_lock(&iio_dev_opaque->info_exist_lock); if (!chan->indio_dev->info) { @@ -904,7 +927,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) goto err_unlock; } - ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); + ret = iio_channel_read_max(chan, val, NULL, IIO_CHAN_INFO_RAW); err_unlock: mutex_unlock(&iio_dev_opaque->info_exist_lock); diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index c0deff5d42824a..aed853ebe1d1cf 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -46,36 +47,43 @@ static void adc_joystick_poll(struct input_dev *input) static int adc_joystick_handle(const void *data, void *private) { struct adc_joystick *joy = private; + struct iio_buffer *buffer; enum iio_endian endianness; - int bytes, msb, val, idx, i; - const u16 *data_u16; + int bytes, msb, val, off; + const u8 *chan_data; + unsigned int i; bool sign; bytes = joy->chans[0].channel->scan_type.storagebits >> 3; for (i = 0; i < joy->num_chans; ++i) { - idx = joy->chans[i].channel->scan_index; endianness = joy->chans[i].channel->scan_type.endianness; msb = joy->chans[i].channel->scan_type.realbits - 1; sign = tolower(joy->chans[i].channel->scan_type.sign) == 's'; + buffer = iio_channel_cb_get_iio_buffer(joy->buffer); + off = iio_find_channel_offset_in_buffer(joy->chans[i].indio_dev, + joy->chans[i].channel, + buffer); + if (off < 0) + return off; + + chan_data = (const u8 *)data + off; switch (bytes) { case 1: - val = ((const u8 *)data)[idx]; + val = *chan_data; break; case 2: - data_u16 = (const u16 *)data + idx; - /* * Data is aligned to the sample size by IIO core. * Call `get_unaligned_xe16` to hide type casting. */ if (endianness == IIO_BE) - val = get_unaligned_be16(data_u16); + val = get_unaligned_be16(chan_data); else if (endianness == IIO_LE) - val = get_unaligned_le16(data_u16); + val = get_unaligned_le16(chan_data); else /* IIO_CPU */ - val = *data_u16; + val = *(const u16 *)chan_data; break; default: return -EINVAL; diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 616a38feb641e5..1ac6af61e7c3df 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -29,6 +29,18 @@ config RADIO_MAXIRADIO To compile this driver as a module, choose M here: the module will be called radio-maxiradio. +config RADIO_RDA5807 + tristate "RDA5807 I2C FM radio support" + depends on I2C + select REGMAP_I2C + select PM + help + Say Y here if you want to use the RDA5807 FM receiver connected to + an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called radio-rda5807. + config RADIO_SAA7706H tristate "SAA7706H Car Radio DSP" depends on I2C diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index cfb6af7d3bc329..f0163314cf1804 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_ISA) += radio-isa.o obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o +obj-$(CONFIG_RADIO_RDA5807) += radio-rda5807.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o diff --git a/drivers/media/radio/radio-rda5807.c b/drivers/media/radio/radio-rda5807.c new file mode 100644 index 00000000000000..57e3fa9e553972 --- /dev/null +++ b/drivers/media/radio/radio-rda5807.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * radio-rda5807.c - Driver for using the RDA5807 FM tuner chip via I2C + * + * Copyright (c) 2011 Maarten ter Huurne + * Copyright (c) 2021 Paul Cercueil + * + * Many thanks to Jérôme Veres for his command line radio application that + * demonstrates how the chip can be controlled via I2C. + * + * Also thanks to Marcos Paulo de Souza for several patches to this driver. + * + * The RDA5807 has three ways of accessing registers: + * - I2C address 0x10: sequential access, RDA5800 style + * - I2C address 0x11: random access + * - I2C address 0x60: sequential access, TEA5767 compatible + * + * This driver only supports random access to the registers. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +enum rda5807_reg { + RDA5807_REG_CHIPID = 0x00, + RDA5807_REG_CTRL = 0x02, + RDA5807_REG_CHAN = 0x03, + RDA5807_REG_IOCFG = 0x04, + RDA5807_REG_INPUT = 0x05, + RDA5807_REG_BAND = 0x07, + RDA5807_REG_SEEKRES = 0x0A, + RDA5807_REG_SIGNAL = 0x0B, +}; + +#define RDA5807_CTRL_DHIZ BIT(15) +#define RDA5807_CTRL_DMUTE BIT(14) +#define RDA5807_CTRL_MONO BIT(13) +#define RDA5807_CTRL_BASS BIT(12) +#define RDA5807_CTRL_SEEKUP BIT(9) +#define RDA5807_CTRL_SEEK BIT(8) +#define RDA5807_CTRL_SKMODE BIT(7) +#define RDA5807_CTRL_CLKMODE GENMASK(6, 4) +#define RDA5807_CTRL_SOFTRESET BIT(1) +#define RDA5807_CTRL_ENABLE BIT(0) + +#define RDA5807_CHAN_WRCHAN GENMASK(15, 6) +#define RDA5807_CHAN_TUNE BIT(4) +#define RDA5807_CHAN_BAND GENMASK(3, 2) +#define RDA5807_CHAN_SPACE GENMASK(1, 0) + +#define RDA5807_IOCFG_DEEMPHASIS BIT(11) +#define RDA5807_IOCFG_I2S_EN BIT(6) + +#define RDA5807_INPUT_LNA_PORT GENMASK(7, 6) +#define RDA5807_INPUT_LNA_ICSEL GENMASK(5, 4) +#define RDA5807_INPUT_VOLUME GENMASK(3, 0) + +#define RDA5807_BAND_65M_BAND BIT(9) + +#define RDA5807_SEEKRES_COMPLETE BIT(14) +#define RDA5807_SEEKRES_FAIL BIT(13) +#define RDA5807_SEEKRES_STEREO BIT(10) +#define RDA5807_SEEKRES_READCHAN GENMASK(9, 0) + +#define RDA5807_SIGNAL_RSSI GENMASK(15, 9) + + +#define RDA5807_AUTOSUSPEND_DELAY_MS 5000 + + +struct rda5807_driver { + struct v4l2_ctrl_handler ctrl_handler; + struct video_device video_dev; + struct v4l2_device v4l2_dev; + + struct device *dev; + struct regmap *map; + struct regulator *supply; + + const struct v4l2_frequency_band *band; + + bool unmuted; +}; + +enum rda5807_bands { + RDA5807_BAND_WORLDWIDE, + RDA5807_BAND_EAST_EUROPE, + RDA5807_BAND_UNKNOWN, +}; + +static const struct v4l2_frequency_band rda5807_bands[] = { + [RDA5807_BAND_WORLDWIDE] = { + .index = RDA5807_BAND_WORLDWIDE, + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1216000, /* 76.0 MHz */ + .rangehigh = 1728000, /* 108.0 MHz */ + .modulation = V4L2_BAND_MODULATION_FM, + }, + [RDA5807_BAND_EAST_EUROPE] = { + .index = RDA5807_BAND_EAST_EUROPE, + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 1040000, /* 65.0 MHz */ + .rangehigh = 1216000, /* 76.0 MHz */ + .modulation = V4L2_BAND_MODULATION_FM, + }, + [RDA5807_BAND_UNKNOWN] = { + .index = RDA5807_BAND_UNKNOWN, + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 800000, /* 50.0 MHz */ + .rangehigh = 1040000, /* 65.0 MHz */ + .modulation = V4L2_BAND_MODULATION_FM, + }, +}; + +static const struct v4l2_frequency_band *rda5807_get_band(unsigned long min, + unsigned long max) +{ + const struct v4l2_frequency_band *band; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rda5807_bands); i++) { + band = &rda5807_bands[i]; + + if (band->rangelow <= min && band->rangehigh >= max) + return band; + } + + return NULL; +} + +static int rda5807_set_band(struct rda5807_driver *radio, + const struct v4l2_frequency_band *band) +{ + u16 val; + int err; + + if (band->index == RDA5807_BAND_EAST_EUROPE) + err = regmap_set_bits(radio->map, RDA5807_REG_BAND, + RDA5807_BAND_65M_BAND); + else + err = regmap_clear_bits(radio->map, RDA5807_REG_BAND, + RDA5807_BAND_65M_BAND); + if (err) + return err; + + if (band->index == RDA5807_BAND_WORLDWIDE) + val = FIELD_PREP(RDA5807_CHAN_BAND, 2); + else + val = FIELD_PREP(RDA5807_CHAN_BAND, 3); + + err = regmap_update_bits(radio->map, RDA5807_REG_CHAN, + RDA5807_CHAN_BAND, val); + if (err) + return err; + + radio->band = band; + return 0; +} + +static int rda5807_set_mute(struct rda5807_driver *radio, int muted) +{ + u16 val = muted ? 0 : RDA5807_CTRL_DMUTE /* disable mute */; + + dev_dbg(radio->dev, "set mute to %d\n", muted); + + return regmap_update_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_DMUTE, val); +} + +static int rda5807_set_volume(struct rda5807_driver *radio, int volume) +{ + dev_dbg(radio->dev, "set volume to %d\n", volume); + + return regmap_update_bits(radio->map, RDA5807_REG_INPUT, + RDA5807_INPUT_VOLUME, + FIELD_PREP(RDA5807_INPUT_VOLUME, volume)); +} + +static int rda5807_set_deemphasis(struct rda5807_driver *radio, + enum v4l2_deemphasis deemp) +{ + int err; + + if (deemp == V4L2_DEEMPHASIS_50_uS) + err = regmap_set_bits(radio->map, RDA5807_REG_IOCFG, + RDA5807_IOCFG_DEEMPHASIS); + else + err = regmap_clear_bits(radio->map, RDA5807_REG_IOCFG, + RDA5807_IOCFG_DEEMPHASIS); + + dev_dbg(radio->dev, "set deemphasis to %d\n", deemp); + return err; +} + +static inline struct rda5807_driver *ctrl_to_radio(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct rda5807_driver, ctrl_handler); +} + +static int rda5807_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rda5807_driver *radio = ctrl_to_radio(ctrl); + int err; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (radio->unmuted == !ctrl->val) + break; + + if (ctrl->val) { + pm_runtime_mark_last_busy(radio->dev); + err = pm_runtime_put_autosuspend(radio->dev); + if (err < 0) + return err; + } else { + err = pm_runtime_get_sync(radio->dev); + if (err < 0) { + pm_runtime_put_noidle(radio->dev); + return err; + } + } + + err = rda5807_set_mute(radio, ctrl->val); + if (err) + return err; + + radio->unmuted = !ctrl->val; + break; + case V4L2_CID_AUDIO_VOLUME: + return rda5807_set_volume(radio, ctrl->val); + case V4L2_CID_TUNE_DEEMPHASIS: + return rda5807_set_deemphasis(radio, ctrl->val); + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops rda5807_ctrl_ops = { + .s_ctrl = rda5807_s_ctrl, +}; + +static int rda5807_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + *cap = (struct v4l2_capability) { + .driver = "rda5807", + .card = "RDA5807 FM receiver", + .bus_info = "I2C", + .device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER + | V4L2_CAP_HW_FREQ_SEEK, + }; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int rda5807_vidioc_g_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + + *a = (struct v4l2_audio) { + .name = "Radio", + .capability = V4L2_AUDCAP_STEREO, + .mode = 0, + }; + + return 0; +} + +static int rda5807_vidioc_g_tuner(struct file *file, void *fh, + struct v4l2_tuner *a) +{ + struct rda5807_driver *radio = video_drvdata(file); + unsigned int seekres, signal; + u32 rxsubchans; + int err, active; + + if (a->index != 0) + return -EINVAL; + + active = pm_runtime_get_if_in_use(radio->dev); + if (active < 0) + return active; + + if (active == 0) { + signal = 0; + rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + } else { + err = regmap_read(radio->map, RDA5807_REG_SEEKRES, &seekres); + if (err < 0) + goto out_runtime_pm_put; + + if (seekres & RDA5807_SEEKRES_COMPLETE && + !(seekres & RDA5807_SEEKRES_FAIL)) + /* mono/stereo known */ + rxsubchans = seekres & RDA5807_SEEKRES_STEREO + ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + else + /* mono/stereo unknown */ + rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + + err = regmap_read(radio->map, RDA5807_REG_SIGNAL, &signal); + if (err < 0) + goto out_runtime_pm_put; + } + + *a = (struct v4l2_tuner) { + .name = "FM", + .type = V4L2_TUNER_RADIO, + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO, + /* unit is 1/16 kHz */ + .rangelow = 50000 * 16, + .rangehigh = 108000 * 16, + .rxsubchans = rxsubchans, + /* TODO: Implement forced mono (RDA5807_CTRL_MONO). */ + .audmode = V4L2_TUNER_MODE_STEREO, + .signal = signal & RDA5807_SIGNAL_RSSI, + .afc = 0, /* automatic frequency control */ + }; + + err = 0; +out_runtime_pm_put: + if (active > 0) { + pm_runtime_mark_last_busy(radio->dev); + pm_runtime_put_autosuspend(radio->dev); + } + return err; +} + +static int rda5807_vidioc_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *a) +{ + struct rda5807_driver *radio = video_drvdata(file); + unsigned int val; + int err; + + if (a->tuner != 0) + return -EINVAL; + if (!radio->band) + return -EINVAL; + + err = regmap_read(radio->map, RDA5807_REG_SEEKRES, &val); + if (err < 0) + return err; + + a->frequency = 400 * (val & RDA5807_SEEKRES_READCHAN) + radio->band->rangelow; + return 0; +} + +static int rda5807_vidioc_s_frequency(struct file *file, void *fh, + const struct v4l2_frequency *a) +{ + struct rda5807_driver *radio = video_drvdata(file); + const struct v4l2_frequency_band *band; + u16 mask = 0; + u16 val = 0; + int err, active; + + if (a->tuner != 0) + return -EINVAL; + if (a->type != V4L2_TUNER_RADIO) + return -EINVAL; + + band = rda5807_get_band(a->frequency, a->frequency); + if (!band) + return -ERANGE; + + dev_dbg(radio->dev, "set freq to %u kHz\n", a->frequency / 16); + + err = rda5807_set_band(radio, band); + if (err) + return err; + + /* select 25 kHz channel spacing */ + mask |= RDA5807_CHAN_SPACE; + val |= FIELD_PREP(RDA5807_CHAN_SPACE, 0x3); + + /* select frequency */ + mask |= RDA5807_CHAN_WRCHAN; + val |= FIELD_PREP(RDA5807_CHAN_WRCHAN, (a->frequency + 200) / 400); + + err = regmap_update_bits(radio->map, RDA5807_REG_CHAN, mask, val); + if (err) + return err; + + active = pm_runtime_get_if_in_use(radio->dev); + if (active <= 0) + return active; + + /* start tune operation */ + err = regmap_write_bits(radio->map, RDA5807_REG_CHAN, + RDA5807_CHAN_TUNE, RDA5807_CHAN_TUNE); + + pm_runtime_mark_last_busy(radio->dev); + pm_runtime_put_autosuspend(radio->dev); + + return err; +} + +static int rda5807_vidioc_s_hw_freq_seek(struct file *file, void *fh, + const struct v4l2_hw_freq_seek *a) +{ + struct rda5807_driver *radio = video_drvdata(file); + const struct v4l2_frequency_band *band; + unsigned int val = RDA5807_CTRL_SEEK; + unsigned int freq, spacing, increment; + int err; + + if (a->tuner != 0) + return -EINVAL; + if (a->type != V4L2_TUNER_RADIO) + return -EINVAL; + + switch (a->spacing) { + case 25000: + spacing = 0x3; + break; + case 50000: + spacing = 0x2; + break; + case 100000: + spacing = 0x0; + break; + case 200000: + spacing = 0x1; + break; + default: + return -EINVAL; + } + + band = rda5807_get_band(a->rangelow, a->rangehigh); + if (!band) + return -ERANGE; + + err = pm_runtime_get_sync(radio->dev); + if (err < 0) { + dev_err(radio->dev, "Unable to runtime get: %d\n", err); + pm_runtime_put_noidle(radio->dev); + return err; + } + + /* Configure channel spacing */ + err = regmap_update_bits(radio->map, RDA5807_REG_CHAN, + RDA5807_CHAN_SPACE, + FIELD_PREP(RDA5807_CHAN_SPACE, spacing)); + if (err < 0) + goto out_runtime_pm_put; + + err = rda5807_set_band(radio, band); + if (err < 0) + goto out_runtime_pm_put; + + /* seek up or down? */ + if (a->seek_upward) + val |= RDA5807_CTRL_SEEKUP; + + /* wrap around at band limit? */ + if (!a->wrap_around) + val |= RDA5807_CTRL_SKMODE; + + /* Send seek command */ + err = regmap_update_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_SEEKUP | + RDA5807_CTRL_SKMODE | + RDA5807_CTRL_SEEK, val); + if (err < 0) + goto out_runtime_pm_put; + + increment = a->spacing * 16 / 1000; + + for (freq = a->rangelow; freq <= a->rangehigh; freq += increment) { + /* + * The programming guide says we should wait for 35 ms for each + * frequency tested. + */ + msleep(35); + + err = regmap_read(radio->map, RDA5807_REG_SEEKRES, &val); + if (err < 0) + goto out_runtime_pm_put; + + /* Seek done? */ + if (val & RDA5807_SEEKRES_COMPLETE) + break; + } + + err = regmap_clear_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_SEEK); + if (err) + goto out_runtime_pm_put; + + if (freq > a->rangehigh) + err = -ETIMEDOUT; + +out_runtime_pm_put: + pm_runtime_mark_last_busy(radio->dev); + pm_runtime_put_autosuspend(radio->dev); + return err; +} + +static int rda5807_vidioc_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + if (band->index >= ARRAY_SIZE(rda5807_bands)) + return -EINVAL; + + *band = rda5807_bands[band->index]; + return 0; +} + +static const struct v4l2_ioctl_ops rda5807_ioctl_ops = { + .vidioc_querycap = rda5807_vidioc_querycap, + .vidioc_g_audio = rda5807_vidioc_g_audio, + .vidioc_g_tuner = rda5807_vidioc_g_tuner, + .vidioc_g_frequency = rda5807_vidioc_g_frequency, + .vidioc_s_frequency = rda5807_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = rda5807_vidioc_s_hw_freq_seek, + .vidioc_enum_freq_bands = rda5807_vidioc_enum_freq_bands, +}; + +static const u16 rda5807_lna_current[] = { 1800, 2100, 2500, 3000 }; + +static int rda5807_setup(struct rda5807_driver *radio) +{ + struct device *dev = radio->dev; + u16 lna = 0, iocfg = 0, ctrl = 0; + u32 lna_current = 2500; + size_t i; + int err; + + /* Configure chip inputs. */ + + if (device_property_read_bool(dev, "rda,lnan")) + lna |= 0x1; + if (device_property_read_bool(dev, "rda,lnap")) + lna |= 0x2; + if (!lna) + dev_warn(dev, "Both LNA inputs disabled\n"); + + device_property_read_u32(dev, "rda,lna-microamp", &lna_current); + for (i = 0; i < ARRAY_SIZE(rda5807_lna_current); i++) + if (rda5807_lna_current[i] == lna_current) + break; + if (i == ARRAY_SIZE(rda5807_lna_current)) { + dev_err(dev, "Invalid LNA current value\n"); + return -EINVAL; + } + + err = regmap_update_bits(radio->map, RDA5807_REG_INPUT, + RDA5807_INPUT_LNA_ICSEL | RDA5807_INPUT_LNA_PORT, + FIELD_PREP(RDA5807_INPUT_LNA_ICSEL, i) | + FIELD_PREP(RDA5807_INPUT_LNA_PORT, lna)); + if (err) + return err; + + /* Configure chip outputs. */ + + if (device_property_read_bool(dev, "rda,i2s-out")) + iocfg |= RDA5807_IOCFG_I2S_EN; + + if (device_property_read_bool(dev, "rda,analog-out")) + ctrl |= RDA5807_CTRL_DHIZ; + + err = regmap_write(radio->map, RDA5807_REG_IOCFG, iocfg); + if (err) + return err; + + err = regmap_write(radio->map, RDA5807_REG_CTRL, ctrl); + if (err) + return err; + + return 0; +} + +static int rda5807_enable_regulator(struct rda5807_driver *radio) +{ + int err; + + err = regulator_enable(radio->supply); + if (err) + return err; + + /* A little sleep is needed before the registers can be accessed */ + msleep(20); + + return 0; +} + +static const struct v4l2_file_operations rda5807_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct regmap_range rda5807_no_write_ranges[] = { + { RDA5807_REG_CHIPID, RDA5807_REG_CHIPID }, + { RDA5807_REG_SEEKRES, RDA5807_REG_SIGNAL }, +}; + +static const struct regmap_access_table rda5807_write_table = { + .no_ranges = rda5807_no_write_ranges, + .n_no_ranges = ARRAY_SIZE(rda5807_no_write_ranges), +}; + +static const struct regmap_range rda5807_volatile_ranges[] = { + { RDA5807_REG_SEEKRES, RDA5807_REG_SIGNAL }, +}; + + +static const struct regmap_access_table rda5807_volatile_table = { + .yes_ranges = rda5807_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rda5807_volatile_ranges), +}; + +static const struct reg_default rda5807_reg_defaults[] = { + { RDA5807_REG_CHIPID, 0x5804 }, + { RDA5807_REG_CTRL, 0x0 }, + { RDA5807_REG_CHAN, 0x4fc0 }, + { RDA5807_REG_IOCFG, 0x0400 }, + { RDA5807_REG_INPUT, 0x888b }, + { RDA5807_REG_BAND, 0x5ec6 }, +}; + +static const struct regmap_config rda5807_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = RDA5807_REG_SIGNAL, + + .wr_table = &rda5807_write_table, + .volatile_table = &rda5807_volatile_table, + + .reg_defaults = rda5807_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rda5807_reg_defaults), + + .cache_type = REGCACHE_FLAT, +}; + +static int rda5807_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct rda5807_driver *radio; + unsigned int chipid; + int err; + + radio = devm_kzalloc(dev, sizeof(*radio), GFP_KERNEL); + if (!radio) + return -ENOMEM; + + radio->dev = dev; + + radio->map = devm_regmap_init_i2c(client, &rda5807_regmap_config); + if (IS_ERR(radio->map)) { + dev_err(dev, "Failed to create regmap\n"); + return PTR_ERR(radio->map); + } + + radio->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(radio->supply)) { + dev_err(dev, "Failed to get power supply\n"); + return PTR_ERR(radio->supply); + } + + err = rda5807_enable_regulator(radio); + if (err) { + dev_err(dev, "Failed to enable regulator\n"); + return err; + } + + /* Disable the regmap cache temporarily to force reading the chip ID */ + regcache_cache_bypass(radio->map, true); + err = regmap_read(radio->map, RDA5807_REG_CHIPID, &chipid); + regcache_cache_bypass(radio->map, false); + + regulator_disable(radio->supply); + if (err < 0) { + dev_err(dev, "Failed to read chip ID\n"); + return err; + } + + if ((chipid & 0xFF00) != 0x5800) { + dev_err(dev, "Chip ID mismatch: expected 58xx, got %04X\n", + chipid); + return -ENODEV; + } + + dev_info(dev, "Found FM radio receiver\n"); + + pm_runtime_set_autosuspend_delay(dev, RDA5807_AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + /* Only use regmap cache until the chip is brought up */ + regcache_cache_only(radio->map, true); + regcache_mark_dirty(radio->map); + + err = rda5807_setup(radio); + if (err) { + dev_err(dev, "Failed to setup registers\n"); + return err; + } + + /* Initialize controls. */ + v4l2_ctrl_handler_init(&radio->ctrl_handler, 3); + v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 8); + + v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &rda5807_ctrl_ops, + V4L2_CID_TUNE_DEEMPHASIS, + V4L2_DEEMPHASIS_75_uS, + BIT(V4L2_DEEMPHASIS_DISABLED), + V4L2_DEEMPHASIS_50_uS); + err = radio->ctrl_handler.error; + if (err) { + dev_err(dev, "Failed to init controls handler\n"); + goto err_ctrl_free; + } + + err = v4l2_device_register(dev, &radio->v4l2_dev); + if (err < 0) { + dev_err(dev, "Failed to register v4l2 device\n"); + goto err_ctrl_free; + } + + radio->video_dev = (struct video_device) { + .name = "RDA5807 FM receiver", + .v4l2_dev = &radio->v4l2_dev, + .ctrl_handler = &radio->ctrl_handler, + .fops = &rda5807_fops, + .ioctl_ops = &rda5807_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER + | V4L2_CAP_HW_FREQ_SEEK, + }; + + i2c_set_clientdata(client, radio); + video_set_drvdata(&radio->video_dev, radio); + + err = v4l2_ctrl_handler_setup(&radio->ctrl_handler); + if (err < 0) { + dev_err(dev, "Failed to set default control values\n"); + goto err_video_unreg; + } + + err = video_register_device(&radio->video_dev, VFL_TYPE_RADIO, -1); + if (err < 0) { + dev_err(dev, "Failed to register video device\n"); + goto err_ctrl_free; + } + + return 0; + +err_video_unreg: + video_unregister_device(&radio->video_dev); +err_ctrl_free: + v4l2_ctrl_handler_free(&radio->ctrl_handler); + video_device_release_empty(&radio->video_dev); + + return err; +} + +static void rda5807_i2c_remove(struct i2c_client *client) +{ + struct rda5807_driver *radio = i2c_get_clientdata(client); + struct device *dev = &client->dev; + + pm_runtime_disable(dev); + pm_runtime_force_suspend(dev); + pm_runtime_dont_use_autosuspend(dev); + + video_unregister_device(&radio->video_dev); + v4l2_ctrl_handler_free(&radio->ctrl_handler); + video_device_release_empty(&radio->video_dev); +} + +static int rda5807_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rda5807_driver *radio = i2c_get_clientdata(client); + int err; + + err = regmap_clear_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_ENABLE); + if (err) + return err; + + regcache_cache_only(radio->map, true); + regcache_mark_dirty(radio->map); + + err = regulator_disable(radio->supply); + if (err) + return err; + + dev_dbg(radio->dev, "Disabled\n"); + + return 0; +} + +static int rda5807_reset_chip(struct rda5807_driver *radio) +{ + int err; + + err = regmap_write_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_SOFTRESET, RDA5807_CTRL_SOFTRESET); + if (err) + return err; + + usleep_range(1000, 10000); + + return regmap_write_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_SOFTRESET, 0); +} + +static int rda5807_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rda5807_driver *radio = i2c_get_clientdata(client); + int err; + + err = rda5807_enable_regulator(radio); + if (err) + return err; + + regcache_cache_only(radio->map, false); + + err = rda5807_reset_chip(radio); + if (err) + return err; + + /* Restore cached registers to hardware */ + err = regcache_sync(radio->map); + if (err) { + dev_err(dev, "Failed to restore regs: %d\n", err); + goto err_regulator_disable; + } + + err = regmap_set_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_ENABLE); + if (err) { + dev_err(dev, "Failed to enable radio: %d\n", err); + goto err_regulator_disable; + } + + err = regmap_write_bits(radio->map, RDA5807_REG_CHAN, + RDA5807_CHAN_TUNE, RDA5807_CHAN_TUNE); + if (err) { + dev_err(dev, "Failed to tune radio: %d\n", err); + goto err_radio_disable; + } + + dev_dbg(radio->dev, "Enabled\n"); + + return 0; + +err_radio_disable: + regmap_clear_bits(radio->map, RDA5807_REG_CTRL, + RDA5807_CTRL_ENABLE); +err_regulator_disable: + regulator_disable(radio->supply); + return err; +} + +static UNIVERSAL_DEV_PM_OPS(rda5807_pm_ops, rda5807_suspend, rda5807_resume, NULL); + +static const struct of_device_id rda5807_dt_ids[] = { + { .compatible = "rda,rda5807" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rda5807_dt_ids); + +static const struct i2c_device_id rda5807_id[] = { + { "rda5807", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(i2c, rda5807_id); + +static struct i2c_driver rda5807_i2c_driver = { + .driver = { + .name = "radio-rda5807", + .of_match_table = of_match_ptr(rda5807_dt_ids), + .pm = &rda5807_pm_ops, + }, + .probe = rda5807_i2c_probe, + .remove = rda5807_i2c_remove, + .id_table = rda5807_id, +}; +module_i2c_driver(rda5807_i2c_driver); + +MODULE_AUTHOR("Maarten ter Huurne "); +MODULE_AUTHOR("Paul Cercueil "); +MODULE_DESCRIPTION("RDA5807 FM tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index dc2db9c185ea0d..ccd2c3aed0f0d2 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -149,6 +151,9 @@ struct jz4740_mmc_host { struct platform_device *pdev; struct clk *clk; + struct rw_semaphore clk_rwsem; + struct notifier_block clock_nb; + enum jz4740_mmc_version version; int irq; @@ -158,6 +163,8 @@ struct jz4740_mmc_host { struct mmc_request *req; struct mmc_command *cmd; + bool vqmmc_enabled; + unsigned long waiting; uint32_t cmdat; @@ -373,6 +380,8 @@ static void jz4740_mmc_pre_request(struct mmc_host *mmc, struct jz4740_mmc_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; + down_read(&host->clk_rwsem); + if (!host->use_dma) return; @@ -388,6 +397,8 @@ static void jz4740_mmc_post_request(struct mmc_host *mmc, struct jz4740_mmc_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; + up_read(&host->clk_rwsem); + if (data && data->host_cookie != COOKIE_UNMAPPED) jz4740_mmc_dma_unmap(host, data); @@ -935,6 +946,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct jz4740_mmc_host *host = mmc_priv(mmc); + int ret; + if (ios->clock) jz4740_mmc_set_clock_rate(host, ios->clock); @@ -947,12 +960,25 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_prepare_enable(host->clk); break; case MMC_POWER_ON: + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) + dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n"); + else + host->vqmmc_enabled = true; + } break; - default: + case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } clk_disable_unprepare(host->clk); break; + default: + break; } switch (ios->bus_width) { @@ -978,6 +1004,23 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); } +static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int ret; + + /* vqmmc regulator is available */ + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = mmc_regulator_set_vqmmc(mmc, ios); + return ret < 0 ? ret : 0; + } + + /* no vqmmc regulator, assume fixed regulator at 3/3.3V */ + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + return 0; + + return -EINVAL; +} + static const struct mmc_host_ops jz4740_mmc_ops = { .request = jz4740_mmc_request, .pre_req = jz4740_mmc_pre_request, @@ -986,8 +1029,51 @@ static const struct mmc_host_ops jz4740_mmc_ops = { .get_ro = mmc_gpio_get_ro, .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, + .start_signal_voltage_switch = jz4740_voltage_switch, }; +static inline struct jz4740_mmc_host * +jz4740_mmc_nb_get_priv(struct notifier_block *nb) +{ + return container_of(nb, struct jz4740_mmc_host, clock_nb); +} + +static struct clk *jz4740_mmc_get_parent_clk(struct clk *clk) +{ + /* + * Return the first clock above the one that will effectively modify + * its rate when clk_set_rate(clk) is called. + */ + clk = clk_get_first_to_set_rate(clk); + + return clk_get_parent(clk); +} + +static int jz4740_mmc_update_clk(struct notifier_block *nb, + unsigned long action, + void *data) +{ + struct jz4740_mmc_host *host = jz4740_mmc_nb_get_priv(nb); + + /* + * PLL may have changed its frequency; our clock may be running above + * spec. Wait until MMC is idle (using host->clk_rwsem) before changing + * the PLL clock, and after it's done, reset our clock rate. + */ + + switch (action) { + case PRE_RATE_CHANGE: + down_write(&host->clk_rwsem); + break; + default: + clk_set_rate(host->clk, host->mmc->f_max); + up_write(&host->clk_rwsem); + break; + } + + return NOTIFY_OK; +} + static const struct of_device_id jz4740_mmc_of_match[] = { { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, @@ -1005,6 +1091,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) struct mmc_host *mmc; struct jz4740_mmc_host *host; const struct of_device_id *match; + struct clk *parent_clk; mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); if (!mmc) { @@ -1053,6 +1140,16 @@ static int jz4740_mmc_probe(struct platform_device* pdev) mmc->ops = &jz4740_mmc_ops; if (!mmc->f_max) mmc->f_max = JZ_MMC_CLK_RATE; + + /* + * There seems to be a problem with this driver on the JZ4760 and + * JZ4760B SoCs. There, when using the maximum rate supported (50 MHz), + * the communication fails with many SD cards. + * Until this bug is sorted out, limit the maximum rate to 24 MHz. + */ + if (host->version == JZ_MMC_JZ4760 && mmc->f_max > JZ_MMC_CLK_RATE) + mmc->f_max = JZ_MMC_CLK_RATE; + mmc->f_min = mmc->f_max / 128; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; @@ -1091,12 +1188,23 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_irq; host->use_dma = !ret; + init_rwsem(&host->clk_rwsem); + host->clock_nb.notifier_call = jz4740_mmc_update_clk; + + parent_clk = jz4740_mmc_get_parent_clk(host->clk); + + ret = clk_notifier_register(parent_clk, &host->clock_nb); + if (ret) { + dev_err(&pdev->dev, "Unable to register clock notifier\n"); + goto err_release_dma; + } + platform_set_drvdata(pdev, host); ret = mmc_add_host(mmc); if (ret) { dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); - goto err_release_dma; + goto err_unregister_clk_notifier; } dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n"); @@ -1107,6 +1215,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) return 0; +err_unregister_clk_notifier: + clk_notifier_unregister(parent_clk, &host->clock_nb); err_release_dma: if (host->use_dma) jz4740_mmc_release_dma_channels(host); diff --git a/drivers/phy/ingenic/phy-ingenic-usb.c b/drivers/phy/ingenic/phy-ingenic-usb.c index 28c28d81648492..655c4997de499d 100644 --- a/drivers/phy/ingenic/phy-ingenic-usb.c +++ b/drivers/phy/ingenic/phy-ingenic-usb.c @@ -362,13 +362,37 @@ static int ingenic_usb_phy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(provider); } +#define IF_ENABLED(cfg, ptr) PTR_IF(IS_ENABLED(cfg), ptr) + static const struct of_device_id ingenic_usb_phy_of_matches[] = { - { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info }, - { .compatible = "ingenic,jz4775-phy", .data = &jz4775_soc_info }, - { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info }, - { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info }, - { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info }, - { .compatible = "ingenic,x2000-phy", .data = &x2000_soc_info }, + { + .compatible = "ingenic,jz4760-phy", + .data = IF_ENABLED(CONFIG_MACH_JZ4760, &jz4770_soc_info), + }, + { + .compatible = "ingenic,jz4770-phy", + .data = IF_ENABLED(CONFIG_MACH_JZ4770, &jz4770_soc_info), + }, + { + .compatible = "ingenic,jz4775-phy", + .data = IF_ENABLED(CONFIG_MACH_JZ4775, &jz4775_soc_info), + }, + { + .compatible = "ingenic,jz4780-phy", + .data = IF_ENABLED(CONFIG_MACH_JZ4780, &jz4780_soc_info), + }, + { + .compatible = "ingenic,x1000-phy", + .data = IF_ENABLED(CONFIG_MACH_X1000, &x1000_soc_info), + }, + { + .compatible = "ingenic,x1830-phy", + .data = IF_ENABLED(CONFIG_MACH_X1830, &x1830_soc_info), + }, + { + .compatible = "ingenic,x2000-phy", + .data = IF_ENABLED(CONFIG_MACH_X2000, &x2000_soc_info), + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index 2e7fdfde47ece1..50bd1a48453a79 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -55,6 +55,18 @@ static int ingenic_battery_get_property(struct power_supply *psy, } } +static inline bool scale_type_supported(int type) +{ + switch (type) { + case IIO_VAL_INT_PLUS_NANO: + case IIO_VAL_INT_PLUS_MICRO: + case IIO_VAL_FRACTIONAL_LOG2: + return true; + default: + return false; + } +} + /* Set the most appropriate IIO channel voltage reference scale * based on the battery's max voltage. */ @@ -62,7 +74,8 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat) { const int *scale_raw; int scale_len, scale_type, best_idx = -1, best_mV, max_raw, i, ret; - u64 max_mV; + unsigned int offset; + u64 max_mV, scale_mV; ret = iio_read_max_channel_raw(bat->channel, &max_raw); if (ret) { @@ -77,13 +90,48 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat) dev_err(bat->dev, "Unable to read channel avail scale\n"); return ret; } - if (ret != IIO_AVAIL_LIST || scale_type != IIO_VAL_FRACTIONAL_LOG2) + + switch (ret) { + case IIO_AVAIL_LIST: + if (!scale_type_supported(scale_type)) { + dev_err(bat->dev, "Unsupported scale type\n"); + return -EINVAL; + } + + offset = 2; + break; + case IIO_AVAIL_LIST_WITH_TYPE: + for (i = 0; i < scale_len; i += 3) { + if (!scale_type_supported(scale_raw[i + 2])) { + dev_err(bat->dev, "Unsupported scale type\n"); + return -EINVAL; + } + } + + offset = 3; + scale_type = scale_raw[2]; + break; + default: + dev_err(bat->dev, "Unsupported scale format\n"); return -EINVAL; + } max_mV = bat->info->voltage_max_design_uv / 1000; - for (i = 0; i < scale_len; i += 2) { - u64 scale_mV = (max_raw * scale_raw[i]) >> scale_raw[i + 1]; + for (i = 0; i < scale_len; i += offset) { + switch (scale_type) { + case IIO_VAL_INT_PLUS_MICRO: + scale_mV = max_raw * scale_raw[i] + + max_raw * scale_raw[i + 1] / 1000; + break; + case IIO_VAL_INT_PLUS_NANO: + scale_mV = max_raw * scale_raw[i] + + max_raw * scale_raw[i + 1] / 1000000; + break; + case IIO_VAL_FRACTIONAL_LOG2: + scale_mV = (max_raw * scale_raw[i]) >> scale_raw[i + 1]; + break; + } if (scale_mV < max_mV) continue; @@ -101,7 +149,7 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat) } /* Only set scale if there is more than one (fractional) entry */ - if (scale_len > 2) { + if (scale_len > offset) { ret = iio_write_channel_attribute(bat->channel, scale_raw[best_idx], scale_raw[best_idx + 1], diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 60d13a949bc58c..cb623d0702f6d2 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -282,8 +282,8 @@ config PWM_IQS620A config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" - depends on MIPS || COMPILE_TEST - depends on COMMON_CLK + depends on MACH_INGENIC || COMPILE_TEST + depends on COMMON_CLK && OF select MFD_SYSCON help Generic PWM framework driver for Ingenic JZ47xx based diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index a5fdf97c0d2ec6..21071bd63a322e 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -27,6 +27,7 @@ struct soc_info { struct jz4740_pwm_chip { struct pwm_chip chip; struct regmap *map; + u32 pwm_channels_mask; }; static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) @@ -37,14 +38,7 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz, unsigned int channel) { - /* Enable all TCU channels for PWM use by default except channels 0/1 */ - u32 pwm_channels_mask = GENMASK(jz->chip.npwm - 1, 2); - - device_property_read_u32(jz->chip.dev->parent, - "ingenic,pwm-channels-mask", - &pwm_channels_mask); - - return !!(pwm_channels_mask & BIT(channel)); + return !!(jz->pwm_channels_mask & BIT(channel)); } static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -88,8 +82,7 @@ static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) struct jz4740_pwm_chip *jz = to_jz4740(chip); /* Enable PWM output */ - regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN); + regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN); /* Start counter */ regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm)); @@ -97,24 +90,39 @@ static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } +static void jz4740_pwm_set_polarity(struct jz4740_pwm_chip *jz, + unsigned int hwpwm, + enum pwm_polarity polarity) +{ + unsigned int value = 0; + + if (polarity == PWM_POLARITY_INVERSED) + value = TCU_TCSR_PWM_INITL_HIGH; + + regmap_update_bits(jz->map, TCU_REG_TCSRc(hwpwm), + TCU_TCSR_PWM_INITL_HIGH, value); +} + static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct jz4740_pwm_chip *jz = to_jz4740(chip); /* - * Set duty > period. This trick allows the TCU channels in TCU2 mode to - * properly return to their init level. + * Set duty > period, then enable PWM mode and start the counter. + * This trick allows to force the inactive pin level for the TCU2 + * channels. */ regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff); regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0); + regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN); + regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm)); /* * Disable PWM output. * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the * counter is stopped, while in TCU1 mode the order does not matter. */ - regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_EN, 0); + regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN); /* Stop counter */ regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm)); @@ -127,6 +135,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long tmp = 0xffffull * NSEC_PER_SEC; struct clk *clk = pwm_get_chip_data(pwm); unsigned long period, duty; + enum pwm_polarity polarity; long rate; int err; @@ -166,6 +175,9 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (duty >= period) duty = period - 1; + /* Restore regular polarity before disabling the channel. */ + jz4740_pwm_set_polarity(jz4740, pwm->hwpwm, state->polarity); + jz4740_pwm_disable(chip, pwm); err = clk_set_rate(clk, rate); @@ -184,32 +196,33 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period); /* Set abrupt shutdown */ - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD); + regmap_set_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_SD); + + if (state->enabled) { + /* + * Set polarity. + * + * The PWM starts in inactive state until the internal timer + * reaches the duty value, then becomes active until the timer + * reaches the period value. In theory, we should then use + * (period - duty) as the real duty value, as a high duty value + * would otherwise result in the PWM pin being inactive most of + * the time. + * + * Here, we don't do that, and instead invert the polarity of + * the PWM when it is active. This trick makes the PWM start + * with its active state instead of its inactive state. + */ + if (state->polarity == PWM_POLARITY_NORMAL) + polarity = PWM_POLARITY_INVERSED; + else + polarity = PWM_POLARITY_NORMAL; + + jz4740_pwm_set_polarity(jz4740, pwm->hwpwm, polarity); - /* - * Set polarity. - * - * The PWM starts in inactive state until the internal timer reaches the - * duty value, then becomes active until the timer reaches the period - * value. In theory, we should then use (period - duty) as the real duty - * value, as a high duty value would otherwise result in the PWM pin - * being inactive most of the time. - * - * Here, we don't do that, and instead invert the polarity of the PWM - * when it is active. This trick makes the PWM start with its active - * state instead of its inactive state. - */ - if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled) - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, 0); - else - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, - TCU_TCSR_PWM_INITL_HIGH); - - if (state->enabled) jz4740_pwm_enable(chip, pwm); + } return 0; } @@ -235,6 +248,13 @@ static int jz4740_pwm_probe(struct platform_device *pdev) if (!jz4740) return -ENOMEM; + /* Enable all TCU channels for PWM use by default except channels 0/1 */ + jz4740->pwm_channels_mask = GENMASK(jz4740->chip.npwm - 1, 2); + + device_property_read_u32(dev->parent, + "ingenic,pwm-channels-mask", + &jz4740->pwm_channels_mask); + jz4740->map = device_node_to_regmap(dev->parent->of_node); if (IS_ERR(jz4740->map)) { dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map)); @@ -248,19 +268,18 @@ static int jz4740_pwm_probe(struct platform_device *pdev) return devm_pwmchip_add(dev, &jz4740->chip); } -static const struct soc_info __maybe_unused jz4740_soc_info = { +static const struct soc_info jz4740_soc_info = { .num_pwms = 8, }; -static const struct soc_info __maybe_unused jz4725b_soc_info = { +static const struct soc_info jz4725b_soc_info = { .num_pwms = 6, }; -static const struct soc_info __maybe_unused x1000_soc_info = { +static const struct soc_info x1000_soc_info = { .num_pwms = 5, }; -#ifdef CONFIG_OF static const struct of_device_id jz4740_pwm_dt_ids[] = { { .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info }, { .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info }, @@ -268,12 +287,11 @@ static const struct of_device_id jz4740_pwm_dt_ids[] = { {}, }; MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids); -#endif static struct platform_driver jz4740_pwm_driver = { .driver = { .name = "jz4740-pwm", - .of_match_table = of_match_ptr(jz4740_pwm_dt_ids), + .of_match_table = jz4740_pwm_dt_ids, }, .probe = jz4740_pwm_probe, }; diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c index 9902cce28692c5..82eed93905af6d 100644 --- a/drivers/remoteproc/ingenic_rproc.c +++ b/drivers/remoteproc/ingenic_rproc.c @@ -184,7 +184,19 @@ static int ingenic_rproc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vpu); mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux"); - vpu->aux_base = devm_ioremap_resource(dev, mem); + + /* + * Request only the registers we use. + * Regs 0x4->0xc will be used in a hwspinlock driver. + */ + if (!devm_request_mem_region(dev, mem->start, 0x4, dev_name(dev)) || + !devm_request_mem_region(dev, mem->start + REG_AUX_MSG_ACK, + 0x10, dev_name(dev))) { + dev_err(dev, "unable to request I/O memory region\n"); + return -EBUSY; + } + + vpu->aux_base = devm_ioremap(dev, mem->start, resource_size(mem)); if (IS_ERR(vpu->aux_base)) { dev_err(dev, "Failed to ioremap\n"); return PTR_ERR(vpu->aux_base); diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index c383719292c7d1..d1a004546014fd 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -5,8 +5,11 @@ * JZ4740 SoC RTC driver */ +#include #include +#include #include +#include #include #include #include @@ -25,6 +28,7 @@ #define JZ_REG_RTC_WAKEUP_FILTER 0x24 #define JZ_REG_RTC_RESET_COUNTER 0x28 #define JZ_REG_RTC_SCRATCHPAD 0x34 +#define JZ_REG_RTC_CKPCR 0x40 /* The following are present on the jz4780 */ #define JZ_REG_RTC_WENR 0x3C @@ -38,16 +42,22 @@ #define JZ_RTC_CTRL_AE BIT(2) #define JZ_RTC_CTRL_ENABLE BIT(0) +#define JZ_RTC_REGULATOR_NC1HZ_MASK GENMASK(15, 0) +#define JZ_RTC_REGULATOR_ADJC_MASK GENMASK(25, 16) + /* Magic value to enable writes on jz4780 */ #define JZ_RTC_WENR_MAGIC 0xA55A #define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 #define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 +#define JZ_RTC_CKPCR_CK32PULL_DIS BIT(4) +#define JZ_RTC_CKPCR_CK32CTL_EN (BIT(2) | BIT(1)) + enum jz4740_rtc_type { ID_JZ4740, ID_JZ4760, - ID_JZ4780, + ID_JZ4770, }; struct jz4740_rtc { @@ -55,6 +65,9 @@ struct jz4740_rtc { enum jz4740_rtc_type type; struct rtc_device *rtc; + struct clk *clk; + + struct clk_hw clk32k; spinlock_t lock; }; @@ -69,19 +82,15 @@ static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) { uint32_t ctrl; - int timeout = 10000; - - do { - ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); - } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); - return timeout ? 0 : -EIO; + return readl_poll_timeout(rtc->base + JZ_REG_RTC_CTRL, ctrl, + ctrl & JZ_RTC_CTRL_WRDY, 0, 1000); } static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) { uint32_t ctrl; - int ret, timeout = 10000; + int ret; ret = jz4740_rtc_wait_write_ready(rtc); if (ret != 0) @@ -89,11 +98,8 @@ static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); - do { - ctrl = readl(rtc->base + JZ_REG_RTC_WENR); - } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout); - - return timeout ? 0 : -EIO; + return readl_poll_timeout(rtc->base + JZ_REG_RTC_WENR, ctrl, + ctrl & JZ_RTC_WENR_WEN, 0, 1000); } static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, @@ -216,12 +222,51 @@ static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); } +static int jz4740_rtc_read_offset(struct device *dev, long *offset) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + long rate = clk_get_rate(rtc->clk); + s32 nc1hz, adjc, offset1k; + u32 reg; + + reg = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_REGULATOR); + nc1hz = FIELD_GET(JZ_RTC_REGULATOR_NC1HZ_MASK, reg); + adjc = FIELD_GET(JZ_RTC_REGULATOR_ADJC_MASK, reg); + + offset1k = (nc1hz - rate + 1) * 1024L + adjc; + *offset = offset1k * 1000000L / (rate * 1024L); + + return 0; +} + +static int jz4740_rtc_set_offset(struct device *dev, long offset) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + long rate = clk_get_rate(rtc->clk); + s32 offset1k, adjc, nc1hz; + + offset1k = div_s64_rem(offset * rate * 1024LL, 1000000LL, &adjc); + nc1hz = rate - 1 + offset1k / 1024L; + + if (adjc < 0) { + nc1hz--; + adjc += 1024; + } + + nc1hz = FIELD_PREP(JZ_RTC_REGULATOR_NC1HZ_MASK, nc1hz); + adjc = FIELD_PREP(JZ_RTC_REGULATOR_ADJC_MASK, adjc); + + return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, nc1hz | adjc); +} + static const struct rtc_class_ops jz4740_rtc_ops = { .read_time = jz4740_rtc_read_time, .set_time = jz4740_rtc_set_time, .read_alarm = jz4740_rtc_read_alarm, .set_alarm = jz4740_rtc_set_alarm, .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, + .read_offset = jz4740_rtc_read_offset, + .set_offset = jz4740_rtc_set_offset, }; static irqreturn_t jz4740_rtc_irq(int irq, void *data) @@ -260,7 +305,8 @@ static void jz4740_rtc_power_off(void) static const struct of_device_id jz4740_rtc_of_match[] = { { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, { .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 }, - { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, + { .compatible = "ingenic,jz4770-rtc", .data = (void *)ID_JZ4770 }, + { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4770 }, {}, }; MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); @@ -301,6 +347,38 @@ static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc, jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks); } +static int jz4740_rtc_clk32k_enable(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + + return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, + JZ_RTC_CKPCR_CK32PULL_DIS | + JZ_RTC_CKPCR_CK32CTL_EN); +} + +static void jz4740_rtc_clk32k_disable(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, 0); +} + +static int jz4740_rtc_clk32k_is_enabled(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + u32 ckpcr; + + ckpcr = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CKPCR); + + return !!(ckpcr & JZ_RTC_CKPCR_CK32CTL_EN); +} + +static const struct clk_ops jz4740_rtc_clk32k_ops = { + .enable = jz4740_rtc_clk32k_enable, + .disable = jz4740_rtc_clk32k_disable, + .is_enabled = jz4740_rtc_clk32k_is_enabled, +}; + static int jz4740_rtc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -330,6 +408,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) spin_lock_init(&rtc->lock); + rtc->clk = clk; platform_set_drvdata(pdev, rtc); device_init_wakeup(dev, 1); @@ -376,6 +455,23 @@ static int jz4740_rtc_probe(struct platform_device *pdev) dev_warn(dev, "Poweroff handler already present!\n"); } + if (rtc->type == ID_JZ4770) { + rtc->clk32k.init = CLK_HW_INIT_HW("clk32k", __clk_get_hw(clk), + &jz4740_rtc_clk32k_ops, 0); + + ret = devm_clk_hw_register(dev, &rtc->clk32k); + if (ret) { + dev_err(dev, "Unable to register clk32k clock: %d\n", ret); + return ret; + } + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &rtc->clk32k); + if (ret) { + dev_err(dev, "Unable to register clk32k clock provider: %d\n", ret); + return ret; + } + } + return 0; } diff --git a/include/linux/clk.h b/include/linux/clk.h index 1ef0133242374b..eebca63e7e53e8 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -864,6 +864,17 @@ struct clk *clk_get_parent(struct clk *clk); */ struct clk *clk_get_sys(const char *dev_id, const char *con_id); +/** + * clk_get_first_to_set_rate - get a pointer to the clock that will + * effectively modify its rate when clk_set_rate(clk) is called + * (might be clk itself, or any ancestor) + * @clk: clock source + * + * Returns struct clk corresponding to the matched clock source, or + * NULL on error. + */ +struct clk *clk_get_first_to_set_rate(struct clk *clk); + /** * clk_save_context - save clock context for poweroff * @@ -1050,6 +1061,11 @@ static inline struct clk *clk_get_parent(struct clk *clk) return NULL; } +static inline struct clk *clk_get_first_to_set_rate(struct clk *clk) +{ + return NULL; +} + static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id) { return NULL; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f7c49bbdb8a185..d84e0e99f084e4 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -189,6 +189,7 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 *values); int i2c_get_device_id(const struct i2c_client *client, struct i2c_device_identity *id); +const struct i2c_device_id *i2c_client_get_device_id(const struct i2c_client *client); #endif /* I2C */ /** diff --git a/include/linux/iio/afe/rescale.h b/include/linux/iio/afe/rescale.h index 6eecb435488f11..74de2962f864f8 100644 --- a/include/linux/iio/afe/rescale.h +++ b/include/linux/iio/afe/rescale.h @@ -26,6 +26,8 @@ struct rescale { s32 numerator; s32 denominator; s32 offset; + int scale_len; + int *scale_data; }; int rescale_process_scale(struct rescale *rescale, int scale_type, diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 418b1307d3f2cb..b1db74772e7751 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -16,6 +16,10 @@ enum iio_buffer_direction { IIO_BUFFER_DIRECTION_OUT, }; +int iio_find_channel_offset_in_buffer(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + struct iio_buffer *buffer); + int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data); int iio_pop_from_buffer(struct iio_buffer *buffer, void *data); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 6802596b017c9c..d30207c1a0bfd0 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -196,6 +196,18 @@ struct iio_channel struct iio_dev *iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer); +/** + * iio_channel_cb_get_iio_buffer() - get access to the underlying buffer. + * @cb_buffer: The callback buffer from whom we want the buffer + * information. + * + * This function allows one to obtain information about the buffer. + * The primary aim is to allow drivers that are consuming a buffer to query + * things like channel offsets in the buffer. + */ +struct iio_buffer +*iio_channel_cb_get_iio_buffer(struct iio_cb_buffer *cb_buffer); + /** * iio_read_channel_raw() - read from a given channel * @chan: The channel being queried. @@ -303,7 +315,8 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val); * @vals: Available values read back. * @length: Number of entries in vals. * - * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST. + * Returns an error code, IIO_AVAIL_RANGE, IIO_AVAIL_LIST or + * IIO_AVAIL_LIST_WITH_TYPE. * * For ranges, three vals are always returned; min, step and max. * For lists, all the possible values are enumerated. @@ -322,7 +335,8 @@ int iio_read_avail_channel_raw(struct iio_channel *chan, * @length: Number of entries in vals. * @attribute: info attribute to be read back. * - * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST. + * Returns an error code, IIO_AVAIL_RANGE, IIO_AVAIL_LIST or + * IIO_AVAIL_LIST_WITH_TYPE. */ int iio_read_avail_channel_attribute(struct iio_channel *chan, const int **vals, int *type, int *length, diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 82faa98c719a00..d46aad9cd45f06 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -34,6 +34,7 @@ enum iio_event_info { enum iio_available_type { IIO_AVAIL_LIST, IIO_AVAIL_RANGE, + IIO_AVAIL_LIST_WITH_TYPE, }; enum iio_chan_info_enum { diff --git a/include/linux/slab.h b/include/linux/slab.h index 45efc6c553b826..37fa41af24b605 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -129,7 +129,11 @@ /* The following flags affect the page allocator grouping pages by mobility */ /* Objects are reclaimable */ +#ifndef CONFIG_SLUB_TINY #define SLAB_RECLAIM_ACCOUNT ((slab_flags_t __force)0x00020000U) +#else +#define SLAB_RECLAIM_ACCOUNT ((slab_flags_t __force)0) +#endif #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ /* @@ -336,12 +340,17 @@ enum kmalloc_cache_type { #endif #ifndef CONFIG_MEMCG_KMEM KMALLOC_CGROUP = KMALLOC_NORMAL, -#else - KMALLOC_CGROUP, #endif +#ifdef CONFIG_SLUB_TINY + KMALLOC_RECLAIM = KMALLOC_NORMAL, +#else KMALLOC_RECLAIM, +#endif #ifdef CONFIG_ZONE_DMA KMALLOC_DMA, +#endif +#ifdef CONFIG_MEMCG_KMEM + KMALLOC_CGROUP, #endif NR_KMALLOC_TYPES }; diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index f0ffad6a336531..5834bad8ad78bc 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -80,8 +80,10 @@ struct kmem_cache { unsigned int *random_seq; #endif +#ifdef CONFIG_HARDENED_USERCOPY unsigned int useroffset; /* Usercopy region offset */ unsigned int usersize; /* Usercopy region size */ +#endif struct kmem_cache_node *node[MAX_NUMNODES]; }; diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index f9c68a9dac0429..aa0ee1678d29b3 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -41,6 +41,7 @@ enum stat_item { CPU_PARTIAL_DRAIN, /* Drain cpu partial to node partial */ NR_SLUB_STAT_ITEMS }; +#ifndef CONFIG_SLUB_TINY /* * When changing the layout, make sure freelist and tid are still compatible * with this_cpu_cmpxchg_double() alignment requirements. @@ -57,6 +58,7 @@ struct kmem_cache_cpu { unsigned stat[NR_SLUB_STAT_ITEMS]; #endif }; +#endif /* CONFIG_SLUB_TINY */ #ifdef CONFIG_SLUB_CPU_PARTIAL #define slub_percpu_partial(c) ((c)->partial) @@ -88,7 +90,9 @@ struct kmem_cache_order_objects { * Slab cache management. */ struct kmem_cache { +#ifndef CONFIG_SLUB_TINY struct kmem_cache_cpu __percpu *cpu_slab; +#endif /* Used for retrieving partial slabs, etc. */ slab_flags_t flags; unsigned long min_partial; @@ -136,13 +140,15 @@ struct kmem_cache { struct kasan_cache kasan_info; #endif +#ifdef CONFIG_HARDENED_USERCOPY unsigned int useroffset; /* Usercopy region offset */ unsigned int usersize; /* Usercopy region size */ +#endif struct kmem_cache_node *node[MAX_NUMNODES]; }; -#ifdef CONFIG_SYSFS +#if defined(CONFIG_SYSFS) && !defined(CONFIG_SLUB_TINY) #define SLAB_SUPPORTS_SYSFS void sysfs_slab_unlink(struct kmem_cache *); void sysfs_slab_release(struct kmem_cache *); diff --git a/include/net/ipx.h b/include/net/ipx.h new file mode 100644 index 00000000000000..9d1342807b5970 --- /dev/null +++ b/include/net/ipx.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _NET_INET_IPX_H_ +#define _NET_INET_IPX_H_ +/* + * The following information is in its entirety obtained from: + * + * Novell 'IPX Router Specification' Version 1.10 + * Part No. 107-000029-001 + * + * Which is available from ftp.novell.com + */ + +#include +#include +#include +#include +#include +#include + +struct ipx_address { + __be32 net; + __u8 node[IPX_NODE_LEN]; + __be16 sock; +}; + +#define ipx_broadcast_node "\377\377\377\377\377\377" +#define ipx_this_node "\0\0\0\0\0\0" + +#define IPX_MAX_PPROP_HOPS 8 + +struct ipxhdr { + __be16 ipx_checksum __packed; +#define IPX_NO_CHECKSUM cpu_to_be16(0xFFFF) + __be16 ipx_pktsize __packed; + __u8 ipx_tctrl; + __u8 ipx_type; +#define IPX_TYPE_UNKNOWN 0x00 +#define IPX_TYPE_RIP 0x01 /* may also be 0 */ +#define IPX_TYPE_SAP 0x04 /* may also be 0 */ +#define IPX_TYPE_SPX 0x05 /* SPX protocol */ +#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */ +#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast */ + struct ipx_address ipx_dest __packed; + struct ipx_address ipx_source __packed; +}; + +/* From af_ipx.c */ +extern int sysctl_ipx_pprop_broadcasting; + +struct ipx_interface { + /* IPX address */ + __be32 if_netnum; + unsigned char if_node[IPX_NODE_LEN]; + refcount_t refcnt; + + /* physical device info */ + struct net_device *if_dev; + struct datalink_proto *if_dlink; + __be16 if_dlink_type; + + /* socket support */ + unsigned short if_sknum; + struct hlist_head if_sklist; + spinlock_t if_sklist_lock; + + /* administrative overhead */ + int if_ipx_offset; + unsigned char if_internal; + unsigned char if_primary; + + struct list_head node; /* node in ipx_interfaces list */ +}; + +struct ipx_route { + __be32 ir_net; + struct ipx_interface *ir_intrfc; + unsigned char ir_routed; + unsigned char ir_router_node[IPX_NODE_LEN]; + struct list_head node; /* node in ipx_routes list */ + refcount_t refcnt; +}; + +struct ipx_cb { + u8 ipx_tctrl; + __be32 ipx_dest_net; + __be32 ipx_source_net; + struct { + __be32 netnum; + int index; + } last_hop; +}; + +#include + +struct ipx_sock { + /* struct sock has to be the first member of ipx_sock */ + struct sock sk; + struct ipx_address dest_addr; + struct ipx_interface *intrfc; + __be16 port; +#ifdef CONFIG_IPX_INTERN + unsigned char node[IPX_NODE_LEN]; +#endif + unsigned short type; + /* + * To handle special ncp connection-handling sockets for mars_nwe, + * the connection number must be stored in the socket. + */ + unsigned short ipx_ncp_conn; +}; + +static inline struct ipx_sock *ipx_sk(struct sock *sk) +{ + return (struct ipx_sock *)sk; +} + +#define IPX_SKB_CB(__skb) ((struct ipx_cb *)&((__skb)->cb[0])) + +#define IPX_MIN_EPHEMERAL_SOCKET 0x4000 +#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff + +extern struct list_head ipx_routes; +extern rwlock_t ipx_routes_lock; + +extern struct list_head ipx_interfaces; +struct ipx_interface *ipx_interfaces_head(void); +extern spinlock_t ipx_interfaces_lock; + +extern struct ipx_interface *ipx_primary_net; + +int ipx_proc_init(void); +void ipx_proc_exit(void); + +const char *ipx_frame_name(__be16); +const char *ipx_device_name(struct ipx_interface *intrfc); + +static __inline__ void ipxitf_hold(struct ipx_interface *intrfc) +{ + refcount_inc(&intrfc->refcnt); +} + +void ipxitf_down(struct ipx_interface *intrfc); +struct ipx_interface *ipxitf_find_using_net(__be32 net); +int ipxitf_send(struct ipx_interface *intrfc, struct sk_buff *skb, char *node); +__be16 ipx_cksum(struct ipxhdr *packet, int length); +int ipxrtr_add_route(__be32 network, struct ipx_interface *intrfc, + unsigned char *node); +void ipxrtr_del_routes(struct ipx_interface *intrfc); +int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, + struct msghdr *msg, size_t len, int noblock); +int ipxrtr_route_skb(struct sk_buff *skb); +struct ipx_route *ipxrtr_lookup(__be32 net); +int ipxrtr_ioctl(unsigned int cmd, void __user *arg); + +static __inline__ void ipxitf_put(struct ipx_interface *intrfc) +{ + if (refcount_dec_and_test(&intrfc->refcnt)) + ipxitf_down(intrfc); +} + +static __inline__ void ipxrtr_hold(struct ipx_route *rt) +{ + refcount_inc(&rt->refcnt); +} + +static __inline__ void ipxrtr_put(struct ipx_route *rt) +{ + if (refcount_dec_and_test(&rt->refcnt)) + kfree(rt); +} +#endif /* _NET_INET_IPX_H_ */ diff --git a/include/uapi/linux/ipx.h b/include/uapi/linux/ipx.h new file mode 100644 index 00000000000000..3168137adae8cf --- /dev/null +++ b/include/uapi/linux/ipx.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IPX_H_ +#define _IPX_H_ +#include /* for compatibility with glibc netipx/ipx.h */ +#include +#include +#include +#define IPX_NODE_LEN 6 +#define IPX_MTU 576 + +#if __UAPI_DEF_SOCKADDR_IPX +struct sockaddr_ipx { + __kernel_sa_family_t sipx_family; + __be16 sipx_port; + __be32 sipx_network; + unsigned char sipx_node[IPX_NODE_LEN]; + __u8 sipx_type; + unsigned char sipx_zero; /* 16 byte fill */ +}; +#endif /* __UAPI_DEF_SOCKADDR_IPX */ + +/* + * So we can fit the extra info for SIOCSIFADDR into the address nicely + */ +#define sipx_special sipx_port +#define sipx_action sipx_zero +#define IPX_DLTITF 0 +#define IPX_CRTITF 1 + +#if __UAPI_DEF_IPX_ROUTE_DEFINITION +struct ipx_route_definition { + __be32 ipx_network; + __be32 ipx_router_network; + unsigned char ipx_router_node[IPX_NODE_LEN]; +}; +#endif /* __UAPI_DEF_IPX_ROUTE_DEFINITION */ + +#if __UAPI_DEF_IPX_INTERFACE_DEFINITION +struct ipx_interface_definition { + __be32 ipx_network; + unsigned char ipx_device[16]; + unsigned char ipx_dlink_type; +#define IPX_FRAME_NONE 0 +#define IPX_FRAME_SNAP 1 +#define IPX_FRAME_8022 2 +#define IPX_FRAME_ETHERII 3 +#define IPX_FRAME_8023 4 +#define IPX_FRAME_TR_8022 5 /* obsolete */ + unsigned char ipx_special; +#define IPX_SPECIAL_NONE 0 +#define IPX_PRIMARY 1 +#define IPX_INTERNAL 2 + unsigned char ipx_node[IPX_NODE_LEN]; +}; +#endif /* __UAPI_DEF_IPX_INTERFACE_DEFINITION */ + +#if __UAPI_DEF_IPX_CONFIG_DATA +struct ipx_config_data { + unsigned char ipxcfg_auto_select_primary; + unsigned char ipxcfg_auto_create_interfaces; +}; +#endif /* __UAPI_DEF_IPX_CONFIG_DATA */ + +/* + * OLD Route Definition for backward compatibility. + */ + +#if __UAPI_DEF_IPX_ROUTE_DEF +struct ipx_route_def { + __be32 ipx_network; + __be32 ipx_router_network; +#define IPX_ROUTE_NO_ROUTER 0 + unsigned char ipx_router_node[IPX_NODE_LEN]; + unsigned char ipx_device[16]; + unsigned short ipx_flags; +#define IPX_RT_SNAP 8 +#define IPX_RT_8022 4 +#define IPX_RT_BLUEBOOK 2 +#define IPX_RT_ROUTED 1 +}; +#endif /* __UAPI_DEF_IPX_ROUTE_DEF */ + +#define SIOCAIPXITFCRT (SIOCPROTOPRIVATE) +#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE + 1) +#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE + 2) +#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE + 3) +#endif /* _IPX_H_ */ diff --git a/kernel/configs/tiny.config b/kernel/configs/tiny.config index 8a44b93da0f3d0..c2f9c912df1cc8 100644 --- a/kernel/configs/tiny.config +++ b/kernel/configs/tiny.config @@ -7,5 +7,6 @@ CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set # CONFIG_KERNEL_LZ4 is not set # CONFIG_SLAB is not set -# CONFIG_SLUB is not set -CONFIG_SLOB=y +# CONFIG_SLOB_DEPRECATED is not set +CONFIG_SLUB=y +CONFIG_SLUB_TINY=y diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index ca09b1cf8ee9d3..836f70393e22ce 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -37,7 +37,7 @@ menuconfig KASAN (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS)) && \ CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \ HAVE_ARCH_KASAN_HW_TAGS - depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB) + depends on (SLUB && SYSFS && !SLUB_TINY) || (SLAB && !DEBUG_SLAB) select STACKDEPOT_ALWAYS_INIT help Enables KASAN (Kernel Address Sanitizer) - a dynamic memory safety diff --git a/mm/Kconfig b/mm/Kconfig index 57e1d8c5b50528..623d95659ff91a 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -219,17 +219,43 @@ config SLUB and has enhanced diagnostics. SLUB is the default choice for a slab allocator. -config SLOB +config SLOB_DEPRECATED depends on EXPERT - bool "SLOB (Simple Allocator)" + bool "SLOB (Simple Allocator - DEPRECATED)" depends on !PREEMPT_RT help + Deprecated and scheduled for removal in a few cycles. SLUB + recommended as replacement. CONFIG_SLUB_TINY can be considered + on systems with 16MB or less RAM. + + If you need SLOB to stay, please contact linux-mm@kvack.org and + people listed in the SLAB ALLOCATOR section of MAINTAINERS file, + with your use case. + SLOB replaces the stock allocator with a drastically simpler allocator. SLOB is generally more space efficient but does not perform as well on large systems. endchoice +config SLOB + bool + default y + depends on SLOB_DEPRECATED + +config SLUB_TINY + bool "Configure SLUB for minimal memory footprint" + depends on SLUB && EXPERT + select SLAB_MERGE_DEFAULT + help + Configures the SLUB allocator in a way to achieve minimal memory + footprint, sacrificing scalability, debugging and other features. + This is intended only for the smallest system that had used the + SLOB allocator and is not recommended for systems with more than + 16MB RAM. + + If unsure, say N. + config SLAB_MERGE_DEFAULT bool "Allow slab caches to be merged" default y @@ -247,7 +273,7 @@ config SLAB_MERGE_DEFAULT config SLAB_FREELIST_RANDOM bool "Randomize slab freelist" - depends on SLAB || SLUB + depends on SLAB || (SLUB && !SLUB_TINY) help Randomizes the freelist order used on creating new pages. This security feature reduces the predictability of the kernel slab @@ -255,7 +281,7 @@ config SLAB_FREELIST_RANDOM config SLAB_FREELIST_HARDENED bool "Harden slab freelist metadata" - depends on SLAB || SLUB + depends on SLAB || (SLUB && !SLUB_TINY) help Many kernel heap attacks try to target slab cache metadata and other infrastructure. This options makes minor performance @@ -267,7 +293,7 @@ config SLAB_FREELIST_HARDENED config SLUB_STATS default n bool "Enable SLUB performance statistics" - depends on SLUB && SYSFS + depends on SLUB && SYSFS && !SLUB_TINY help SLUB statistics are useful to debug SLUBs allocation behavior in order find ways to optimize the allocator. This should never be @@ -279,7 +305,7 @@ config SLUB_STATS config SLUB_CPU_PARTIAL default y - depends on SLUB && SMP + depends on SLUB && SMP && !SLUB_TINY bool "SLUB per cpu partial cache" help Per cpu partial caches accelerate objects allocation and freeing diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index ce8dded36de942..fca699ad1fb05f 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -56,7 +56,7 @@ config DEBUG_SLAB config SLUB_DEBUG default y bool "Enable SLUB debugging support" if EXPERT - depends on SLUB && SYSFS + depends on SLUB && SYSFS && !SLUB_TINY select STACKDEPOT if STACKTRACE_SUPPORT help SLUB has extensive debug support features. Disabling these can diff --git a/mm/slab.h b/mm/slab.h index 0202a8c2f0d25d..db9a7984e22ef1 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -207,8 +207,6 @@ struct kmem_cache { unsigned int size; /* The aligned/padded/added on size */ unsigned int align; /* Alignment as calculated */ slab_flags_t flags; /* Active flags on the slab */ - unsigned int useroffset;/* Usercopy region offset */ - unsigned int usersize; /* Usercopy region size */ const char *name; /* Slab name for sysfs */ int refcount; /* Use counter */ void (*ctor)(void *); /* Called on object slot creation */ diff --git a/mm/slab_common.c b/mm/slab_common.c index 0042fb2730d1e1..012fc75d3ffa23 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -143,8 +143,10 @@ int slab_unmergeable(struct kmem_cache *s) if (s->ctor) return 1; +#ifdef CONFIG_HARDENED_USERCOPY if (s->usersize) return 1; +#endif /* * We may have set a slab to be unmergeable during bootstrap. @@ -223,8 +225,10 @@ static struct kmem_cache *create_cache(const char *name, s->size = s->object_size = object_size; s->align = align; s->ctor = ctor; +#ifdef CONFIG_HARDENED_USERCOPY s->useroffset = useroffset; s->usersize = usersize; +#endif err = __kmem_cache_create(s, flags); if (err) @@ -317,7 +321,8 @@ kmem_cache_create_usercopy(const char *name, flags &= CACHE_CREATE_MASK; /* Fail closed on bad usersize of useroffset values. */ - if (WARN_ON(!usersize && useroffset) || + if (!IS_ENABLED(CONFIG_HARDENED_USERCOPY) || + WARN_ON(!usersize && useroffset) || WARN_ON(size < usersize || size - usersize < useroffset)) usersize = useroffset = 0; @@ -595,8 +600,8 @@ void kmem_dump_obj(void *object) ptroffset = ((char *)object - (char *)kp.kp_objp) - kp.kp_data_offset; pr_cont(" pointer offset %lu", ptroffset); } - if (kp.kp_slab_cache && kp.kp_slab_cache->usersize) - pr_cont(" size %u", kp.kp_slab_cache->usersize); + if (kp.kp_slab_cache && kp.kp_slab_cache->object_size) + pr_cont(" size %u", kp.kp_slab_cache->object_size); if (kp.kp_ret) pr_cont(" allocated at %pS\n", kp.kp_ret); else @@ -640,8 +645,10 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, align = max(align, size); s->align = calculate_alignment(flags, align, size); +#ifdef CONFIG_HARDENED_USERCOPY s->useroffset = useroffset; s->usersize = usersize; +#endif err = __kmem_cache_create(s, flags); @@ -766,10 +773,16 @@ EXPORT_SYMBOL(kmalloc_size_roundup); #define KMALLOC_CGROUP_NAME(sz) #endif +#ifndef CONFIG_SLUB_TINY +#define KMALLOC_RCL_NAME(sz) .name[KMALLOC_RECLAIM] = "kmalloc-rcl-" #sz, +#else +#define KMALLOC_RCL_NAME(sz) +#endif + #define INIT_KMALLOC_INFO(__size, __short_size) \ { \ .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ - .name[KMALLOC_RECLAIM] = "kmalloc-rcl-" #__short_size, \ + KMALLOC_RCL_NAME(__short_size) \ KMALLOC_CGROUP_NAME(__short_size) \ KMALLOC_DMA_NAME(__short_size) \ .size = __size, \ @@ -855,7 +868,7 @@ void __init setup_kmalloc_cache_index_table(void) static void __init new_kmalloc_cache(int idx, enum kmalloc_cache_type type, slab_flags_t flags) { - if (type == KMALLOC_RECLAIM) { + if ((KMALLOC_RECLAIM != KMALLOC_NORMAL) && (type == KMALLOC_RECLAIM)) { flags |= SLAB_RECLAIM_ACCOUNT; } else if (IS_ENABLED(CONFIG_MEMCG_KMEM) && (type == KMALLOC_CGROUP)) { if (mem_cgroup_kmem_disabled()) { diff --git a/mm/slub.c b/mm/slub.c index 157527d7101be0..ac9e4a15fa32aa 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -187,6 +187,12 @@ do { \ #define USE_LOCKLESS_FAST_PATH() (false) #endif +#ifndef CONFIG_SLUB_TINY +#define __fastpath_inline __always_inline +#else +#define __fastpath_inline +#endif + #ifdef CONFIG_SLUB_DEBUG #ifdef CONFIG_SLUB_DEBUG_ON DEFINE_STATIC_KEY_TRUE(slub_debug_enabled); @@ -241,6 +247,7 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s) /* Enable to log cmpxchg failures */ #undef SLUB_DEBUG_CMPXCHG +#ifndef CONFIG_SLUB_TINY /* * Minimum number of partial slabs. These will be left on the partial * lists even if they are empty. kmem_cache_shrink may reclaim them. @@ -253,6 +260,10 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s) * sort the partial list by the number of objects in use. */ #define MAX_PARTIAL 10 +#else +#define MIN_PARTIAL 0 +#define MAX_PARTIAL 0 +#endif #define DEBUG_DEFAULT_FLAGS (SLAB_CONSISTENCY_CHECKS | SLAB_RED_ZONE | \ SLAB_POISON | SLAB_STORE_USER) @@ -298,7 +309,7 @@ struct track { enum track_item { TRACK_ALLOC, TRACK_FREE }; -#ifdef CONFIG_SYSFS +#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); #else @@ -332,10 +343,12 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si) */ static nodemask_t slab_nodes; +#ifndef CONFIG_SLUB_TINY /* * Workqueue used for flush_cpu_slab(). */ static struct workqueue_struct *flushwq; +#endif /******************************************************************** * Core slab cache functions @@ -381,10 +394,12 @@ static inline void *get_freepointer(struct kmem_cache *s, void *object) return freelist_dereference(s, object + s->offset); } +#ifndef CONFIG_SLUB_TINY static void prefetch_freepointer(const struct kmem_cache *s, void *object) { prefetchw(object + s->offset); } +#endif /* * When running under KMSAN, get_freepointer_safe() may return an uninitialized @@ -1363,7 +1378,7 @@ static inline int alloc_consistency_checks(struct kmem_cache *s, return 1; } -static noinline int alloc_debug_processing(struct kmem_cache *s, +static noinline bool alloc_debug_processing(struct kmem_cache *s, struct slab *slab, void *object, int orig_size) { if (s->flags & SLAB_CONSISTENCY_CHECKS) { @@ -1375,7 +1390,7 @@ static noinline int alloc_debug_processing(struct kmem_cache *s, trace(s, slab, object, 1); set_orig_size(s, object, orig_size); init_object(s, object, SLUB_RED_ACTIVE); - return 1; + return true; bad: if (folio_test_slab(slab_folio(slab))) { @@ -1388,7 +1403,7 @@ static noinline int alloc_debug_processing(struct kmem_cache *s, slab->inuse = slab->objects; slab->freelist = NULL; } - return 0; + return false; } static inline int free_consistency_checks(struct kmem_cache *s, @@ -1641,17 +1656,17 @@ static inline void setup_object_debug(struct kmem_cache *s, void *object) {} static inline void setup_slab_debug(struct kmem_cache *s, struct slab *slab, void *addr) {} -static inline int alloc_debug_processing(struct kmem_cache *s, - struct slab *slab, void *object, int orig_size) { return 0; } +static inline bool alloc_debug_processing(struct kmem_cache *s, + struct slab *slab, void *object, int orig_size) { return true; } -static inline void free_debug_processing( - struct kmem_cache *s, struct slab *slab, - void *head, void *tail, int bulk_cnt, - unsigned long addr) {} +static inline bool free_debug_processing(struct kmem_cache *s, + struct slab *slab, void *head, void *tail, int *bulk_cnt, + unsigned long addr, depot_stack_handle_t handle) { return true; } static inline void slab_pad_check(struct kmem_cache *s, struct slab *slab) {} static inline int check_object(struct kmem_cache *s, struct slab *slab, void *object, u8 val) { return 1; } +static inline depot_stack_handle_t set_track_prepare(void) { return 0; } static inline void set_track(struct kmem_cache *s, void *object, enum track_item alloc, unsigned long addr) {} static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n, @@ -1676,11 +1691,13 @@ static inline void inc_slabs_node(struct kmem_cache *s, int node, static inline void dec_slabs_node(struct kmem_cache *s, int node, int objects) {} +#ifndef CONFIG_SLUB_TINY static bool freelist_corrupted(struct kmem_cache *s, struct slab *slab, void **freelist, void *nextfree) { return false; } +#endif #endif /* CONFIG_SLUB_DEBUG */ /* @@ -2214,7 +2231,7 @@ static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n, if (!pfmemalloc_match(slab, pc->flags)) continue; - if (kmem_cache_debug(s)) { + if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { object = alloc_single_from_partial(s, n, slab, pc->orig_size); if (object) @@ -2329,6 +2346,8 @@ static void *get_partial(struct kmem_cache *s, int node, struct partial_context return get_any_partial(s, pc); } +#ifndef CONFIG_SLUB_TINY + #ifdef CONFIG_PREEMPTION /* * Calculate the next globally unique transaction for disambiguation @@ -2342,7 +2361,7 @@ static void *get_partial(struct kmem_cache *s, int node, struct partial_context * different cpus. */ #define TID_STEP 1 -#endif +#endif /* CONFIG_PREEMPTION */ static inline unsigned long next_tid(unsigned long tid) { @@ -2803,6 +2822,13 @@ static int slub_cpu_dead(unsigned int cpu) return 0; } +#else /* CONFIG_SLUB_TINY */ +static inline void flush_all_cpus_locked(struct kmem_cache *s) { } +static inline void flush_all(struct kmem_cache *s) { } +static inline void __flush_cpu_slab(struct kmem_cache *s, int cpu) { } +static inline int slub_cpu_dead(unsigned int cpu) { return 0; } +#endif /* CONFIG_SLUB_TINY */ + /* * Check if the objects in a per cpu structure fit numa * locality expectations. @@ -2828,38 +2854,28 @@ static inline unsigned long node_nr_objs(struct kmem_cache_node *n) } /* Supports checking bulk free of a constructed freelist */ -static noinline void free_debug_processing( - struct kmem_cache *s, struct slab *slab, - void *head, void *tail, int bulk_cnt, - unsigned long addr) +static inline bool free_debug_processing(struct kmem_cache *s, + struct slab *slab, void *head, void *tail, int *bulk_cnt, + unsigned long addr, depot_stack_handle_t handle) { - struct kmem_cache_node *n = get_node(s, slab_nid(slab)); - struct slab *slab_free = NULL; + bool checks_ok = false; void *object = head; int cnt = 0; - unsigned long flags; - bool checks_ok = false; - depot_stack_handle_t handle = 0; - - if (s->flags & SLAB_STORE_USER) - handle = set_track_prepare(); - - spin_lock_irqsave(&n->list_lock, flags); if (s->flags & SLAB_CONSISTENCY_CHECKS) { if (!check_slab(s, slab)) goto out; } - if (slab->inuse < bulk_cnt) { + if (slab->inuse < *bulk_cnt) { slab_err(s, slab, "Slab has %d allocated objects but %d are to be freed\n", - slab->inuse, bulk_cnt); + slab->inuse, *bulk_cnt); goto out; } next_object: - if (++cnt > bulk_cnt) + if (++cnt > *bulk_cnt) goto out_cnt; if (s->flags & SLAB_CONSISTENCY_CHECKS) { @@ -2881,61 +2897,22 @@ static noinline void free_debug_processing( checks_ok = true; out_cnt: - if (cnt != bulk_cnt) + if (cnt != *bulk_cnt) { slab_err(s, slab, "Bulk free expected %d objects but found %d\n", - bulk_cnt, cnt); - -out: - if (checks_ok) { - void *prior = slab->freelist; - - /* Perform the actual freeing while we still hold the locks */ - slab->inuse -= cnt; - set_freepointer(s, tail, prior); - slab->freelist = head; - - /* - * If the slab is empty, and node's partial list is full, - * it should be discarded anyway no matter it's on full or - * partial list. - */ - if (slab->inuse == 0 && n->nr_partial >= s->min_partial) - slab_free = slab; - - if (!prior) { - /* was on full list */ - remove_full(s, n, slab); - if (!slab_free) { - add_partial(n, slab, DEACTIVATE_TO_TAIL); - stat(s, FREE_ADD_PARTIAL); - } - } else if (slab_free) { - remove_partial(n, slab); - stat(s, FREE_REMOVE_PARTIAL); - } + *bulk_cnt, cnt); + *bulk_cnt = cnt; } - if (slab_free) { - /* - * Update the counters while still holding n->list_lock to - * prevent spurious validation warnings - */ - dec_slabs_node(s, slab_nid(slab_free), slab_free->objects); - } - - spin_unlock_irqrestore(&n->list_lock, flags); +out: if (!checks_ok) slab_fix(s, "Object at 0x%p not freed", object); - if (slab_free) { - stat(s, FREE_SLAB); - free_slab(s, slab_free); - } + return checks_ok; } #endif /* CONFIG_SLUB_DEBUG */ -#if defined(CONFIG_SLUB_DEBUG) || defined(CONFIG_SYSFS) +#if defined(CONFIG_SLUB_DEBUG) || defined(SLAB_SUPPORTS_SYSFS) static unsigned long count_partial(struct kmem_cache_node *n, int (*get_count)(struct slab *)) { @@ -2949,12 +2926,12 @@ static unsigned long count_partial(struct kmem_cache_node *n, spin_unlock_irqrestore(&n->list_lock, flags); return x; } -#endif /* CONFIG_SLUB_DEBUG || CONFIG_SYSFS */ +#endif /* CONFIG_SLUB_DEBUG || SLAB_SUPPORTS_SYSFS */ +#ifdef CONFIG_SLUB_DEBUG static noinline void slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid) { -#ifdef CONFIG_SLUB_DEBUG static DEFINE_RATELIMIT_STATE(slub_oom_rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); int node; @@ -2985,8 +2962,11 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid) pr_warn(" node %d: slabs: %ld, objs: %ld, free: %ld\n", node, nr_slabs, nr_objs, nr_free); } -#endif } +#else /* CONFIG_SLUB_DEBUG */ +static inline void +slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid) { } +#endif static inline bool pfmemalloc_match(struct slab *slab, gfp_t gfpflags) { @@ -2996,6 +2976,7 @@ static inline bool pfmemalloc_match(struct slab *slab, gfp_t gfpflags) return true; } +#ifndef CONFIG_SLUB_TINY /* * Check the slab->freelist and either transfer the freelist to the * per cpu freelist or deactivate the slab. @@ -3283,45 +3264,13 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, return p; } -/* - * If the object has been wiped upon free, make sure it's fully initialized by - * zeroing out freelist pointer. - */ -static __always_inline void maybe_wipe_obj_freeptr(struct kmem_cache *s, - void *obj) -{ - if (unlikely(slab_want_init_on_free(s)) && obj) - memset((void *)((char *)kasan_reset_tag(obj) + s->offset), - 0, sizeof(void *)); -} - -/* - * Inlined fastpath so that allocation functions (kmalloc, kmem_cache_alloc) - * have the fastpath folded into their functions. So no function call - * overhead for requests that can be satisfied on the fastpath. - * - * The fastpath works by first checking if the lockless freelist can be used. - * If not then __slab_alloc is called for slow processing. - * - * Otherwise we can simply pick the next object from the lockless free list. - */ -static __always_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru, +static __always_inline void *__slab_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) { - void *object; struct kmem_cache_cpu *c; struct slab *slab; unsigned long tid; - struct obj_cgroup *objcg = NULL; - bool init = false; - - s = slab_pre_alloc_hook(s, lru, &objcg, 1, gfpflags); - if (!s) - return NULL; - - object = kfence_alloc(s, orig_size, gfpflags); - if (unlikely(object)) - goto out; + void *object; redo: /* @@ -3391,6 +3340,75 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, struct list_l stat(s, ALLOC_FASTPATH); } + return object; +} +#else /* CONFIG_SLUB_TINY */ +static void *__slab_alloc_node(struct kmem_cache *s, + gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) +{ + struct partial_context pc; + struct slab *slab; + void *object; + + pc.flags = gfpflags; + pc.slab = &slab; + pc.orig_size = orig_size; + object = get_partial(s, node, &pc); + + if (object) + return object; + + slab = new_slab(s, gfpflags, node); + if (unlikely(!slab)) { + slab_out_of_memory(s, gfpflags, node); + return NULL; + } + + object = alloc_single_from_new_slab(s, slab, orig_size); + + return object; +} +#endif /* CONFIG_SLUB_TINY */ + +/* + * If the object has been wiped upon free, make sure it's fully initialized by + * zeroing out freelist pointer. + */ +static __always_inline void maybe_wipe_obj_freeptr(struct kmem_cache *s, + void *obj) +{ + if (unlikely(slab_want_init_on_free(s)) && obj) + memset((void *)((char *)kasan_reset_tag(obj) + s->offset), + 0, sizeof(void *)); +} + +/* + * Inlined fastpath so that allocation functions (kmalloc, kmem_cache_alloc) + * have the fastpath folded into their functions. So no function call + * overhead for requests that can be satisfied on the fastpath. + * + * The fastpath works by first checking if the lockless freelist can be used. + * If not then __slab_alloc is called for slow processing. + * + * Otherwise we can simply pick the next object from the lockless free list. + */ +static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru, + gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) +{ + void *object; + struct obj_cgroup *objcg = NULL; + bool init = false; + + s = slab_pre_alloc_hook(s, lru, &objcg, 1, gfpflags); + if (!s) + return NULL; + + object = kfence_alloc(s, orig_size, gfpflags); + if (unlikely(object)) + goto out; + + object = __slab_alloc_node(s, gfpflags, node, addr, orig_size); + maybe_wipe_obj_freeptr(s, object); init = slab_want_init_on_alloc(gfpflags, s); @@ -3400,13 +3418,13 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, struct list_l return object; } -static __always_inline void *slab_alloc(struct kmem_cache *s, struct list_lru *lru, +static __fastpath_inline void *slab_alloc(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags, unsigned long addr, size_t orig_size) { return slab_alloc_node(s, lru, gfpflags, NUMA_NO_NODE, addr, orig_size); } -static __always_inline +static __fastpath_inline void *__kmem_cache_alloc_lru(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags) { @@ -3448,6 +3466,67 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node) } EXPORT_SYMBOL(kmem_cache_alloc_node); +static noinline void free_to_partial_list( + struct kmem_cache *s, struct slab *slab, + void *head, void *tail, int bulk_cnt, + unsigned long addr) +{ + struct kmem_cache_node *n = get_node(s, slab_nid(slab)); + struct slab *slab_free = NULL; + int cnt = bulk_cnt; + unsigned long flags; + depot_stack_handle_t handle = 0; + + if (s->flags & SLAB_STORE_USER) + handle = set_track_prepare(); + + spin_lock_irqsave(&n->list_lock, flags); + + if (free_debug_processing(s, slab, head, tail, &cnt, addr, handle)) { + void *prior = slab->freelist; + + /* Perform the actual freeing while we still hold the locks */ + slab->inuse -= cnt; + set_freepointer(s, tail, prior); + slab->freelist = head; + + /* + * If the slab is empty, and node's partial list is full, + * it should be discarded anyway no matter it's on full or + * partial list. + */ + if (slab->inuse == 0 && n->nr_partial >= s->min_partial) + slab_free = slab; + + if (!prior) { + /* was on full list */ + remove_full(s, n, slab); + if (!slab_free) { + add_partial(n, slab, DEACTIVATE_TO_TAIL); + stat(s, FREE_ADD_PARTIAL); + } + } else if (slab_free) { + remove_partial(n, slab); + stat(s, FREE_REMOVE_PARTIAL); + } + } + + if (slab_free) { + /* + * Update the counters while still holding n->list_lock to + * prevent spurious validation warnings + */ + dec_slabs_node(s, slab_nid(slab_free), slab_free->objects); + } + + spin_unlock_irqrestore(&n->list_lock, flags); + + if (slab_free) { + stat(s, FREE_SLAB); + free_slab(s, slab_free); + } +} + /* * Slow path handling. This may still be called frequently since objects * have a longer lifetime than the cpu slabs in most processing loads. @@ -3473,8 +3552,8 @@ static void __slab_free(struct kmem_cache *s, struct slab *slab, if (kfence_free(head)) return; - if (kmem_cache_debug(s)) { - free_debug_processing(s, slab, head, tail, cnt, addr); + if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) { + free_to_partial_list(s, slab, head, tail, cnt, addr); return; } @@ -3574,6 +3653,7 @@ static void __slab_free(struct kmem_cache *s, struct slab *slab, discard_slab(s, slab); } +#ifndef CONFIG_SLUB_TINY /* * Fastpath with forced inlining to produce a kfree and kmem_cache_free that * can perform fastpath freeing without additional function calls. @@ -3648,8 +3728,18 @@ static __always_inline void do_slab_free(struct kmem_cache *s, } stat(s, FREE_FASTPATH); } +#else /* CONFIG_SLUB_TINY */ +static void do_slab_free(struct kmem_cache *s, + struct slab *slab, void *head, void *tail, + int cnt, unsigned long addr) +{ + void *tail_obj = tail ? : head; -static __always_inline void slab_free(struct kmem_cache *s, struct slab *slab, + __slab_free(s, slab, head, tail_obj, cnt, addr); +} +#endif /* CONFIG_SLUB_TINY */ + +static __fastpath_inline void slab_free(struct kmem_cache *s, struct slab *slab, void *head, void *tail, void **p, int cnt, unsigned long addr) { @@ -3782,18 +3872,13 @@ void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p) } EXPORT_SYMBOL(kmem_cache_free_bulk); -/* Note that interrupts must be enabled when calling this function. */ -int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, - void **p) +#ifndef CONFIG_SLUB_TINY +static inline int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, + size_t size, void **p, struct obj_cgroup *objcg) { struct kmem_cache_cpu *c; int i; - struct obj_cgroup *objcg = NULL; - /* memcg and kmem_cache debug support */ - s = slab_pre_alloc_hook(s, NULL, &objcg, size, flags); - if (unlikely(!s)) - return false; /* * Drain objects in the per cpu slab, while disabling local * IRQs, which protects against PREEMPT and interrupts @@ -3847,18 +3932,71 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, local_unlock_irq(&s->cpu_slab->lock); slub_put_cpu_ptr(s->cpu_slab); - /* - * memcg and kmem_cache debug support and memory initialization. - * Done outside of the IRQ disabled fastpath loop. - */ - slab_post_alloc_hook(s, objcg, flags, size, p, - slab_want_init_on_alloc(flags, s)); return i; + error: slub_put_cpu_ptr(s->cpu_slab); slab_post_alloc_hook(s, objcg, flags, i, p, false); kmem_cache_free_bulk(s, i, p); return 0; + +} +#else /* CONFIG_SLUB_TINY */ +static int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, + size_t size, void **p, struct obj_cgroup *objcg) +{ + int i; + + for (i = 0; i < size; i++) { + void *object = kfence_alloc(s, s->object_size, flags); + + if (unlikely(object)) { + p[i] = object; + continue; + } + + p[i] = __slab_alloc_node(s, flags, NUMA_NO_NODE, + _RET_IP_, s->object_size); + if (unlikely(!p[i])) + goto error; + + maybe_wipe_obj_freeptr(s, p[i]); + } + + return i; + +error: + slab_post_alloc_hook(s, objcg, flags, i, p, false); + kmem_cache_free_bulk(s, i, p); + return 0; +} +#endif /* CONFIG_SLUB_TINY */ + +/* Note that interrupts must be enabled when calling this function. */ +int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, + void **p) +{ + int i; + struct obj_cgroup *objcg = NULL; + + if (!size) + return 0; + + /* memcg and kmem_cache debug support */ + s = slab_pre_alloc_hook(s, NULL, &objcg, size, flags); + if (unlikely(!s)) + return 0; + + i = __kmem_cache_alloc_bulk(s, flags, size, p, objcg); + + /* + * memcg and kmem_cache debug support and memory initialization. + * Done outside of the IRQ disabled fastpath loop. + */ + if (i != 0) + slab_post_alloc_hook(s, objcg, flags, size, p, + slab_want_init_on_alloc(flags, s)); + return i; } EXPORT_SYMBOL(kmem_cache_alloc_bulk); @@ -3883,7 +4021,8 @@ EXPORT_SYMBOL(kmem_cache_alloc_bulk); * take the list_lock. */ static unsigned int slub_min_order; -static unsigned int slub_max_order = PAGE_ALLOC_COSTLY_ORDER; +static unsigned int slub_max_order = + IS_ENABLED(CONFIG_SLUB_TINY) ? 1 : PAGE_ALLOC_COSTLY_ORDER; static unsigned int slub_min_objects; /* @@ -4014,6 +4153,7 @@ init_kmem_cache_node(struct kmem_cache_node *n) #endif } +#ifndef CONFIG_SLUB_TINY static inline int alloc_kmem_cache_cpus(struct kmem_cache *s) { BUILD_BUG_ON(PERCPU_DYNAMIC_EARLY_SIZE < @@ -4033,6 +4173,12 @@ static inline int alloc_kmem_cache_cpus(struct kmem_cache *s) return 1; } +#else +static inline int alloc_kmem_cache_cpus(struct kmem_cache *s) +{ + return 1; +} +#endif /* CONFIG_SLUB_TINY */ static struct kmem_cache *kmem_cache_node; @@ -4095,7 +4241,9 @@ static void free_kmem_cache_nodes(struct kmem_cache *s) void __kmem_cache_release(struct kmem_cache *s) { cache_random_seq_destroy(s); +#ifndef CONFIG_SLUB_TINY free_percpu(s->cpu_slab); +#endif free_kmem_cache_nodes(s); } @@ -4872,8 +5020,10 @@ void __init kmem_cache_init(void) void __init kmem_cache_init_late(void) { +#ifndef CONFIG_SLUB_TINY flushwq = alloc_workqueue("slub_flushwq", WQ_MEM_RECLAIM, 0); WARN_ON(!flushwq); +#endif } struct kmem_cache * @@ -4924,7 +5074,7 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) return 0; } -#ifdef CONFIG_SYSFS +#ifdef SLAB_SUPPORTS_SYSFS static int count_inuse(struct slab *slab) { return slab->inuse; @@ -5182,7 +5332,7 @@ static void process_slab(struct loc_track *t, struct kmem_cache *s, #endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_SLUB_DEBUG */ -#ifdef CONFIG_SYSFS +#ifdef SLAB_SUPPORTS_SYSFS enum slab_stat_type { SL_ALL, /* All slabs */ SL_PARTIAL, /* Only partially allocated slabs */ @@ -5502,11 +5652,13 @@ static ssize_t cache_dma_show(struct kmem_cache *s, char *buf) SLAB_ATTR_RO(cache_dma); #endif +#ifdef CONFIG_HARDENED_USERCOPY static ssize_t usersize_show(struct kmem_cache *s, char *buf) { return sysfs_emit(buf, "%u\n", s->usersize); } SLAB_ATTR_RO(usersize); +#endif static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf) { @@ -5803,7 +5955,9 @@ static struct attribute *slab_attrs[] = { #ifdef CONFIG_FAILSLAB &failslab_attr.attr, #endif +#ifdef CONFIG_HARDENED_USERCOPY &usersize_attr.attr, +#endif #ifdef CONFIG_KFENCE &skip_kfence_attr.attr, #endif @@ -6056,7 +6210,7 @@ static int __init slab_sysfs_init(void) } __initcall(slab_sysfs_init); -#endif /* CONFIG_SYSFS */ +#endif /* SLAB_SUPPORTS_SYSFS */ #if defined(CONFIG_SLUB_DEBUG) && defined(CONFIG_DEBUG_FS) static int slab_debugfs_show(struct seq_file *seq, void *v) diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig index e72f826062e962..dd3b4507fbe6da 100644 --- a/sound/soc/jz4740/Kconfig +++ b/sound/soc/jz4740/Kconfig @@ -3,6 +3,7 @@ config SND_JZ4740_SOC_I2S tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC" depends on MIPS || COMPILE_TEST depends on HAS_IOMEM + select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740 diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index c4c1e89b47c1b9..3091feeaac3ec4 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -3,19 +3,19 @@ * Copyright (C) 2010, Lars-Peter Clausen */ +#include +#include +#include +#include #include #include #include #include #include #include +#include #include -#include -#include - -#include - #include #include #include @@ -33,67 +33,60 @@ #define JZ_REG_AIC_CLK_DIV 0x30 #define JZ_REG_AIC_FIFO 0x34 -#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12) -#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8) -#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6) -#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5) -#define JZ_AIC_CONF_I2S BIT(4) -#define JZ_AIC_CONF_RESET BIT(3) -#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2) -#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1) -#define JZ_AIC_CONF_ENABLE BIT(0) - -#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 -#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 -#define JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 -#define JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 - -#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) -#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) -#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15) -#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14) -#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) -#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) -#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) +#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6) +#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5) +#define JZ_AIC_CONF_I2S BIT(4) +#define JZ_AIC_CONF_RESET BIT(3) +#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2) +#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1) +#define JZ_AIC_CONF_ENABLE BIT(0) + +#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE GENMASK(21, 19) +#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE GENMASK(18, 16) +#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15) +#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14) +#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) +#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) +#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) #define JZ_AIC_CTRL_FLUSH BIT(8) -#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) -#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) -#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) -#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3) -#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2) -#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1) -#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0) - -#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19 -#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16 - -#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) -#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) -#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) -#define JZ_AIC_I2S_FMT_MSB BIT(0) - -#define JZ_AIC_I2S_STATUS_BUSY BIT(2) - -#define JZ_AIC_CLK_DIV_MASK 0xf -#define I2SDIV_DV_SHIFT 0 -#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT) -#define I2SDIV_IDV_SHIFT 8 -#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT) - -enum jz47xx_i2s_version { - JZ_I2S_JZ4740, - JZ_I2S_JZ4760, - JZ_I2S_JZ4770, - JZ_I2S_JZ4780, -}; +#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) +#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) +#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) +#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3) +#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2) +#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1) +#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0) + +#define JZ4760_AIC_CTRL_TFLUSH BIT(8) +#define JZ4760_AIC_CTRL_RFLUSH BIT(7) + +#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) +#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) +#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) +#define JZ_AIC_I2S_FMT_MSB BIT(0) + +#define JZ_AIC_I2S_STATUS_BUSY BIT(2) struct i2s_soc_info { - enum jz47xx_i2s_version version; struct snd_soc_dai_driver *dai; + + struct reg_field field_rx_fifo_thresh; + struct reg_field field_tx_fifo_thresh; + struct reg_field field_i2sdiv_capture; + struct reg_field field_i2sdiv_playback; + + const char *pll_clk_name; + + bool shared_fifo_flush; }; struct jz4740_i2s { - void __iomem *base; + struct regmap *regmap; + + struct regmap_field *field_rx_fifo_thresh; + struct regmap_field *field_tx_fifo_thresh; + struct regmap_field *field_i2sdiv_capture; + struct regmap_field *field_i2sdiv_playback; struct clk *clk_aic; struct clk *clk_i2s; @@ -104,40 +97,38 @@ struct jz4740_i2s { const struct i2s_soc_info *soc_info; }; -static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, - unsigned int reg) -{ - return readl(i2s->base + reg); -} - -static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s, - unsigned int reg, uint32_t value) -{ - writel(value, i2s->base + reg); -} - static int jz4740_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - uint32_t conf, ctrl; int ret; + /* + * When we can flush FIFOs independently, only flush the + * FIFO that is starting up. + */ + if (!i2s->soc_info->shared_fifo_flush) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ4760_AIC_CTRL_TFLUSH); + else + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ4760_AIC_CTRL_RFLUSH); + } + if (snd_soc_dai_active(dai)) return 0; - ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); - ctrl |= JZ_AIC_CTRL_FLUSH; - jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); + /* + * When there is a shared flush bit for both FIFOs we can + * only flush the FIFOs if no other stream has started. + */ + if (i2s->soc_info->shared_fifo_flush) + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_FLUSH); ret = clk_prepare_enable(i2s->clk_i2s); if (ret) return ret; - conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); - conf |= JZ_AIC_CONF_ENABLE; - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); return 0; } @@ -145,14 +136,11 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - uint32_t conf; if (snd_soc_dai_active(dai)) return; - conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); - conf &= ~JZ_AIC_CONF_ENABLE; - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); + regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); clk_disable_unprepare(i2s->clk_i2s); } @@ -161,8 +149,6 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - uint32_t ctrl; uint32_t mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -170,38 +156,30 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, else mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA; - ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ctrl |= mask; + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ctrl &= ~mask; + regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask); break; default: return -EINVAL; } - jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); - return 0; } static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - uint32_t format = 0; - uint32_t conf; - - conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); - - conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER); + const unsigned int conf_mask = JZ_AIC_CONF_BIT_CLK_MASTER | + JZ_AIC_CONF_SYNC_CLK_MASTER; + unsigned int conf = 0, format = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: @@ -237,8 +215,8 @@ static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format); + regmap_update_bits(i2s->regmap, JZ_REG_AIC_CONF, conf_mask, conf); + regmap_write(i2s->regmap, JZ_REG_AIC_I2S_FMT, format); return 0; } @@ -247,51 +225,51 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct regmap_field *div_field; unsigned int sample_size; - uint32_t ctrl, div_reg; + uint32_t ctrl; int div; - ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); + regmap_read(i2s->regmap, JZ_REG_AIC_CTRL, &ctrl); - div_reg = jz4740_i2s_read(i2s, JZ_REG_AIC_CLK_DIV); div = clk_get_rate(i2s->clk_i2s) / (64 * params_rate(params)); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: sample_size = 0; break; - case SNDRV_PCM_FORMAT_S16: + case SNDRV_PCM_FORMAT_S16_LE: sample_size = 1; break; + case SNDRV_PCM_FORMAT_S20_LE: + sample_size = 3; + break; + case SNDRV_PCM_FORMAT_S24_LE: + sample_size = 4; + break; default: return -EINVAL; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK; - ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET; + ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE; + ctrl |= FIELD_PREP(JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE, sample_size); + if (params_channels(params) == 1) ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; else ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; - div_reg &= ~I2SDIV_DV_MASK; - div_reg |= (div - 1) << I2SDIV_DV_SHIFT; + div_field = i2s->field_i2sdiv_playback; } else { - ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; - ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; - - if (i2s->soc_info->version >= JZ_I2S_JZ4770) { - div_reg &= ~I2SDIV_IDV_MASK; - div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; - } else { - div_reg &= ~I2SDIV_DV_MASK; - div_reg |= (div - 1) << I2SDIV_DV_SHIFT; - } + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE; + ctrl |= FIELD_PREP(JZ_AIC_CTRL_INPUT_SAMPLE_SIZE, sample_size); + + div_field = i2s->field_i2sdiv_capture; } - jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); - jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg); + regmap_write(i2s->regmap, JZ_REG_AIC_CTRL, ctrl); + regmap_field_write(div_field, div - 1); return 0; } @@ -311,7 +289,7 @@ static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, clk_set_parent(i2s->clk_i2s, parent); break; case JZ4740_I2S_CLKSRC_PLL: - parent = clk_get(NULL, "pll half"); + parent = clk_get(NULL, i2s->soc_info->pll_clk_name); if (IS_ERR(parent)) return PTR_ERR(parent); clk_set_parent(i2s->clk_i2s, parent); @@ -325,87 +303,13 @@ static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, return ret; } -static int jz4740_i2s_suspend(struct snd_soc_component *component) -{ - struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); - uint32_t conf; - - if (snd_soc_component_active(component)) { - conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); - conf &= ~JZ_AIC_CONF_ENABLE; - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - - clk_disable_unprepare(i2s->clk_i2s); - } - - clk_disable_unprepare(i2s->clk_aic); - - return 0; -} - -static int jz4740_i2s_resume(struct snd_soc_component *component) -{ - struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); - uint32_t conf; - int ret; - - ret = clk_prepare_enable(i2s->clk_aic); - if (ret) - return ret; - - if (snd_soc_component_active(component)) { - ret = clk_prepare_enable(i2s->clk_i2s); - if (ret) { - clk_disable_unprepare(i2s->clk_aic); - return ret; - } - - conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); - conf |= JZ_AIC_CONF_ENABLE; - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - } - - return 0; -} - static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - uint32_t conf; - int ret; - - ret = clk_prepare_enable(i2s->clk_aic); - if (ret) - return ret; snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, &i2s->capture_dma_data); - if (i2s->soc_info->version >= JZ_I2S_JZ4760) { - conf = (7 << JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | - (8 << JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | - JZ_AIC_CONF_OVERFLOW_PLAY_LAST | - JZ_AIC_CONF_I2S | - JZ_AIC_CONF_INTERNAL_CODEC; - } else { - conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | - (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | - JZ_AIC_CONF_OVERFLOW_PLAY_LAST | - JZ_AIC_CONF_I2S | - JZ_AIC_CONF_INTERNAL_CODEC; - } - - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); - jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - - return 0; -} - -static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai) -{ - struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - clk_disable_unprepare(i2s->clk_aic); return 0; } @@ -419,21 +323,22 @@ static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { }; #define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE) + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver jz4740_i2s_dai = { .probe = jz4740_i2s_dai_probe, - .remove = jz4740_i2s_dai_remove, .playback = { .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = JZ4740_I2S_FMTS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = JZ4740_I2S_FMTS, }, .symmetric_rate = 1, @@ -441,45 +346,127 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = { }; static const struct i2s_soc_info jz4740_i2s_soc_info = { - .version = JZ_I2S_JZ4740, - .dai = &jz4740_i2s_dai, + .dai = &jz4740_i2s_dai, + .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 12, 15), + .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 8, 11), + .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .pll_clk_name = "pll half", + .shared_fifo_flush = true, }; static const struct i2s_soc_info jz4760_i2s_soc_info = { - .version = JZ_I2S_JZ4760, - .dai = &jz4740_i2s_dai, + .dai = &jz4740_i2s_dai, + .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), + .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), + .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .pll_clk_name = "pll half", }; static struct snd_soc_dai_driver jz4770_i2s_dai = { .probe = jz4740_i2s_dai_probe, - .remove = jz4740_i2s_dai_remove, .playback = { .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = JZ4740_I2S_FMTS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = JZ4740_I2S_FMTS, }, .ops = &jz4740_i2s_dai_ops, }; static const struct i2s_soc_info jz4770_i2s_soc_info = { - .version = JZ_I2S_JZ4770, - .dai = &jz4770_i2s_dai, + .dai = &jz4770_i2s_dai, + .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), + .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), + .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11), + .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .pll_clk_name = "pll half", }; static const struct i2s_soc_info jz4780_i2s_soc_info = { - .version = JZ_I2S_JZ4780, - .dai = &jz4770_i2s_dai, + .dai = &jz4770_i2s_dai, + .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), + .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), + .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11), + .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), + .pll_clk_name = "pll half", }; +static int jz4740_i2s_suspend(struct snd_soc_component *component) +{ + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); + + if (snd_soc_component_active(component)) { + regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); + clk_disable_unprepare(i2s->clk_i2s); + } + + clk_disable_unprepare(i2s->clk_aic); + + return 0; +} + +static int jz4740_i2s_resume(struct snd_soc_component *component) +{ + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); + int ret; + + ret = clk_prepare_enable(i2s->clk_aic); + if (ret) + return ret; + + if (snd_soc_component_active(component)) { + ret = clk_prepare_enable(i2s->clk_i2s); + if (ret) { + clk_disable_unprepare(i2s->clk_aic); + return ret; + } + + regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); + } + + return 0; +} + +static int jz4740_i2s_probe(struct snd_soc_component *component) +{ + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); + int ret; + + ret = clk_prepare_enable(i2s->clk_aic); + if (ret) + return ret; + + regmap_write(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); + + regmap_write(i2s->regmap, JZ_REG_AIC_CONF, + JZ_AIC_CONF_OVERFLOW_PLAY_LAST | + JZ_AIC_CONF_I2S | JZ_AIC_CONF_INTERNAL_CODEC); + + regmap_field_write(i2s->field_rx_fifo_thresh, 7); + regmap_field_write(i2s->field_tx_fifo_thresh, 8); + + return 0; +} + +static void jz4740_i2s_remove(struct snd_soc_component *component) +{ + struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(i2s->clk_aic); +} + static const struct snd_soc_component_driver jz4740_i2s_component = { .name = "jz4740-i2s", + .probe = jz4740_i2s_probe, + .remove = jz4740_i2s_remove, .suspend = jz4740_i2s_suspend, .resume = jz4740_i2s_resume, .legacy_dai_naming = 1, @@ -494,12 +481,50 @@ static const struct of_device_id jz4740_of_matches[] = { }; MODULE_DEVICE_TABLE(of, jz4740_of_matches); +static int jz4740_i2s_init_regmap_fields(struct device *dev, + struct jz4740_i2s *i2s) +{ + i2s->field_rx_fifo_thresh = + devm_regmap_field_alloc(dev, i2s->regmap, + i2s->soc_info->field_rx_fifo_thresh); + if (IS_ERR(i2s->field_rx_fifo_thresh)) + return PTR_ERR(i2s->field_rx_fifo_thresh); + + i2s->field_tx_fifo_thresh = + devm_regmap_field_alloc(dev, i2s->regmap, + i2s->soc_info->field_tx_fifo_thresh); + if (IS_ERR(i2s->field_tx_fifo_thresh)) + return PTR_ERR(i2s->field_tx_fifo_thresh); + + i2s->field_i2sdiv_capture = + devm_regmap_field_alloc(dev, i2s->regmap, + i2s->soc_info->field_i2sdiv_capture); + if (IS_ERR(i2s->field_i2sdiv_capture)) + return PTR_ERR(i2s->field_i2sdiv_capture); + + i2s->field_i2sdiv_playback = + devm_regmap_field_alloc(dev, i2s->regmap, + i2s->soc_info->field_i2sdiv_playback); + if (IS_ERR(i2s->field_i2sdiv_playback)) + return PTR_ERR(i2s->field_i2sdiv_playback); + + return 0; +} + +static const struct regmap_config jz4740_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = JZ_REG_AIC_FIFO, +}; + static int jz4740_i2s_dev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct jz4740_i2s *i2s; struct resource *mem; - int ret; + void __iomem *regs; + int ret, flags = 0; i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) @@ -507,9 +532,9 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) i2s->soc_info = device_get_match_data(dev); - i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); - if (IS_ERR(i2s->base)) - return PTR_ERR(i2s->base); + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); i2s->playback_dma_data.maxburst = 16; i2s->playback_dma_data.addr = mem->start + JZ_REG_AIC_FIFO; @@ -525,6 +550,15 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) if (IS_ERR(i2s->clk_i2s)) return PTR_ERR(i2s->clk_i2s); + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &jz4740_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) + return PTR_ERR(i2s->regmap); + + ret = jz4740_i2s_init_regmap_fields(dev, i2s); + if (ret) + return ret; + platform_set_drvdata(pdev, i2s); ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component, @@ -532,8 +566,10 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) if (ret) return ret; - return devm_snd_dmaengine_pcm_register(dev, NULL, - SND_DMAENGINE_PCM_FLAG_COMPAT); + if (device_property_present(dev, "rx-tx")) + flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; + + return devm_snd_dmaengine_pcm_register(dev, NULL, flags); } static struct platform_driver jz4740_i2s_driver = {