diff options
131 files changed, 14556 insertions, 2952 deletions
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 3435d138955c..45d052ad0ada 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -80,6 +80,9 @@ its hardware characteristcs. * qcom,force-reg-dump: enables TMC reg dump support + * arm,sg-enable : indicates whether scatter gather feature is enabled + by default for TMC ETR configuration. + * Required property for TPDAs: * qcom,tpda-atid: must be present. Specifies the ATID for TPDA. diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt new file mode 100644 index 000000000000..0b65b776645b --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -0,0 +1,119 @@ +QTI MDSS DP + +MDSS DP is a display-port driver which supports panels that are compatible with +VESA DP and EDP display interface specification. + +When configuring the optional properties for external backlight, one should also +configure the gpio that drives the pwm to it. + +Required properties +- compatible : Must be "qcom,mdss-edp". +- reg : Offset and length of the register set for the + device. +- reg-names : Names to refer to register sets related to this + device +- gdsc-supply : Phandle for gdsc regulator device node. +- vdda-1p2-supply : Phandle for 1.2V vdda regulator device node. +- vdda-0p9-supply : Phandle for 0.9V vdda regulator device node. +- status : A string that has to be set to "okay/ok" to enable + the driver. By default this property will be set to + "disable". Will be set to "ok/okay" status for + specific platforms. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the + interface is mapped. +- clocks: List of Phandles for clock device nodes + needed by the device. +- clock-names: List of clock names needed by the device. + +Optional properties: +- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the + a particular "type" of DSI modulee. The module "types" + can be "core", "ctrl", and "phy". Within the same type, + there can be more than one instance of this binding, + in which case the entry would be appended with the + supply entry index. + e.g. qcom,ctrl-supply-entry@0 + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + +Example: + mdss_dp_ctrl: qcom,dp_ctrl@c990000 { + cell-index = <0>; + compatible = "qcom,mdss-dp"; + qcom,mdss-fb-map = <&mdss_fb3>; + + gdsc-supply = <&gdsc_mdss>; + vdda-1p2-supply = <&pmcobalt_l2>; + vdda-0p9-supply = <&pmcobalt_l1>; + + reg = <0xc990000 0xa84>, + <0xc011000 0x910>, + <0x1fcb200 0x050>; + reg-names = "dp_ctrl", "dp_phy", "tcsr_regs"; + + clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_mmss clk_mmss_mdss_axi_clk>, + <&clock_mmss clk_mmss_mdss_mdp_clk>, + <&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>, + <&clock_mmss clk_mmss_mdss_dp_aux_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_mmss clk_mmss_mdss_dp_link_clk>, + <&clock_mmss clk_mmss_mdss_dp_link_intf_clk>, + <&clock_mmss clk_mmss_mdss_dp_crypto_clk>, + <&clock_mmss clk_mmss_mdss_dp_pixel_clk>; + clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk", + "core_mdp_core_clk", "core_alt_iface_clk", + "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk", + "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk"; + + qcom,core-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,core-supply-entry@0 { + reg = <0>; + qcom,supply-name = "gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + + qcom,ctrl-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,ctrl-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <12560>; + qcom,supply-disable-load = <4>; + }; + }; + + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <73400>; + qcom,supply-disable-load = <32>; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt deleted file mode 100644 index 882047a43fc9..000000000000 --- a/Documentation/devicetree/bindings/fb/mdss-edp.txt +++ /dev/null @@ -1,52 +0,0 @@ -Qualcomm MDSS EDP - -MDSS EDP is a edp driver which supports panels that are compatable with -VESA EDP display interface specification. - -When configuring the optional properties for external backlight, one should also -configure the gpio that drives the pwm to it. - -Required properties -- compatible : Must be "qcom,mdss-edp". -- reg : Offset and length of the register set for the - device. -- reg-names : Names to refer to register sets related to this - device -- vdda-supply : Phandle for vdd regulator device node. -- gpio-panel-en : GPIO for supplying power to panel and backlight - driver. -- gpio-lvl-en : GPIO to enable HPD be received by host. -- status : A string that has to be set to "okay/ok" to enable - the driver. By default this property will be set to - "disable". Will be set to "ok/okay" status for - specific platforms. -- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the - interface is mapped. -- gpio-panel-hpd : gpio pin use for edp hpd - -Optional properties -- qcom,panel-lpg-channel : LPG channel for backlight. -- qcom,panel-pwm-period : PWM period in microseconds. - - -Optional properties: -- qcom,mdss-brightness-max-level: Specifies the max brightness level supported. - 255 = default value. - -Example: - mdss_edp: qcom,mdss_edp@fd923400 { - compatible = "qcom,mdss-edp"; - reg = <0xfd923400 0x700>, - <0xfd8c2000 0x1000>; - reg-names = "edp_base", "mmss_cc_base"; - vdda-supply = <&pm8941_l12>; - gpio-panel-en = <&msmgpio 58 0>; - gpio-lvl-en = <&msmgpio 91 0>; - qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */ - qcom,panel-pwm-period = <53>; - status = "disable"; - qcom,mdss-fb-map = <&mdss_fb0>; - gpio-panel-hpd = <&msmgpio 102 0>; - }; - - diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index ce2e38b905a1..06b8c71effcc 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -138,6 +138,11 @@ Optional Properties: Specify the size of snapshot in bytes. This will override snapshot size defined in the driver code. +- qcom,gpu-qdss-stm: + <baseAddr size> + baseAddr - base address of the gpu channels in the qdss stm memory region + size - size of the gpu stm region + GPU Quirks: - qcom,gpu-quirk-two-pass-use-wfi: Signal the GPU to set Set TWOPASSUSEWFI bit in diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt new file mode 100644 index 000000000000..9bf6d5b2bf8e --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -0,0 +1,179 @@ +Qualcomm Techonologies, Inc. QPNP PMIC Fuel Gauge Gen3 Device + +QPNP PMIC FG Gen3 device provides interface to the clients to read properties +related to the battery. Its main function is to retrieve the State of Charge +(SOC), in percentage scale representing the amount of charge left in the +battery. + +======================= +Required Node Structure +======================= + +FG Gen3 device must be described in two levels of device nodes. The first +level describes the FG Gen3 device. The second level describes one or more +peripherals managed by FG Gen3 driver. All the peripheral specific parameters +such as base address, interrupts etc., should be under second level node. + +==================================== +First Level Node - FG Gen3 device +==================================== + +- compatible + Usage: required + Value type: <string> + Definition: Should be "qcom,fg-gen3". + +- qcom,pmic-revid + Usage: required + Value type: <phandle> + Definition: Should specify the phandle of PMIC revid module. This is + used to identify the PMIC subtype. + +- io-channels +- io-channel-names + Usage: required + Value type: <phandle> + Definition: For details about IIO bindings see: + Documentation/devicetree/bindings/iio/iio-bindings.txt + +- qcom,fg-cutoff-voltage + Usage: optional + Value type: <u32> + Definition: The voltage (in mV) where the fuel gauge will steer the SOC + to be zero. For example, if the cutoff voltage is set to + 3400mv, the fuel gauge will try to count SoC so that the + battery SOC will be 0 when it is 3400mV. If this property + is not specified, then the default value used will be + 3200mV. + +- qcom,fg-empty-voltage + Usage: optional + Value type: <u32> + Definition: The voltage threshold (in mV) based on which the empty soc + interrupt will be triggered. When the empty soc interrupt + fires, battery soc will be set to 0 and the userspace will + be notified via the power supply framework. The userspace + will read 0% soc and immediately shutdown. If this property + is not specified, then the default value used will be + 3100mV. + +- qcom,fg-vbatt-low-thr + Usage: optional + Value type: <u32> + Definition: The voltage threshold (in mV) which upon set will be used + for configuring the low battery voltage threshold. + +- qcom,fg-chg-term-current + Usage: optional + Value type: <u32> + Definition: Battery current (in mA) at which the fuel gauge will issue + an end of charge if the charger is configured to use the + fuel gauge ADC for end of charge detection. If this + property is not specified, then the default value used + will be 100mA. + +- qcom,fg-sys-term-current + Usage: optional + Value type: <u32> + Definition: Battery current (in mA) at which the fuel gauge will try to + scale towards 100%. When the charge current goes above this + the SOC should be at 100%. If this property is not + specified, then the default value used will be 125mA. + +- qcom,fg-delta-soc-thr + Usage: optional + Value type: <u32> + Definition: Percentage of monotonic SOC increase upon which the delta + SOC interrupt will be triggered. If this property is not + specified, then the default value will be 1. + +- qcom,fg-recharge-soc-thr + Usage: optional + Value type: <u32> + Definition: Percentage of monotonic SOC upon which the charging will + will be resumed once the charging is complete. If this + property is not specified, then the default value will be + 95. + +- qcom,fg-rsense-sel + Usage: optional + Value type: <u32> + Definition: Specifies the source of sense resistor. + Allowed values are: + 0 - Rsense is from Battery FET + 1 - Rsense is external + 2 - Rsense is Battery FET and SMB + Option 2 can be used only when a parallel charger is + present. If this property is not specified, then the + default value will be 2. + +- qcom,fg-jeita-thresholds + Usage: optional + Value type: <prop-encoded-array> + Definition: A list of integers which holds the jeita thresholds (degC) + in the following order. Allowed size is 4. + Element 0 - JEITA cold threshold + Element 1 - JEITA cool threshold + Element 2 - JEITA warm threshold + Element 3 - JEITA hot threshold + If these parameters are not specified, then the default + values used will be 0, 5, 45, 50. + +========================================================== +Second Level Nodes - Peripherals managed by FG Gen3 driver +========================================================== +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the specified peripheral + +- interrupts + Usage: optional + Value type: <prop-encoded-array> + Definition: Interrupt mapping as per the interrupt encoding + +- interrupt-names + Usage: optional + Value type: <stringlist> + Definition: Interrupt names. This list must match up 1-to-1 with the + interrupts specified in the 'interrupts' property. + +======== +Example +======== + +pmicobalt_fg: qpnp,fg { + compatible = "qcom,fg-gen3"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmicobalt_revid>; + io-channels = <&pmicobalt_rradc 3>; + io-channel-names = "rradc_batt_id"; + status = "okay"; + + qcom,fg-batt-soc@4000 { + status = "okay"; + reg = <0x4000 0x100>; + interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "soc-update", + "soc-ready", + "bsoc-delta", + "msoc-delta"; + + }; + + qcom,fg-batt-info@4100 { + status = "okay"; + reg = <0x4100 0x100>; + interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "batt-missing"; + }; + + qcom,fg-memif@4400 { + status = "okay"; + reg = <0x4400 0x100>; + }; +}; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index 35bb94b2ef70..2f82fbfda14f 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -101,6 +101,10 @@ Required properties: Required "supply-name" examples are: "vdd" : vdd supply for SSPHY digital circuit operation "core" : high-voltage analog supply for SSPHY + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "aux_clk" and "pipe_clk". - qcom,vdd-voltage-level: This property must be a list of three integer values (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner @@ -119,6 +123,10 @@ Optional properties: - reg: Additional register set of address and length to control QMP PHY are: "tcsr_usb3_dp_phymode" : top-level CSR register to be written to select super speed usb qmp phy. + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "cfg_ahb_clk", "phy_reset" and "phy_phy_reset". - qcom,vbus-valid-override: If present, indicates VBUS pin is not connected to the USB PHY and the controller must rely on external VBUS notification in order to manually relay the notification to the SSPHY. @@ -138,6 +146,17 @@ Example: vdda18-supply = <&pmd9635_l8>; qcom,vdd-voltage-level = <0 900000 1050000>; qcom,vbus-valid-override; + + clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>, + <&clock_gcc clk_gcc_usb3_phy_pipe_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_gcc clk_gcc_usb3_phy_reset>, + <&clock_gcc clk_gcc_usb3phy_phy_reset>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>; + + clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset", + "phy_phy_reset", "ref_clk_src", "ref_clk"; }; QUSB2 High-Speed PHY @@ -157,7 +176,7 @@ Required properties: - clocks: a list of phandles to the PHY clocks. Use as per Documentation/devicetree/bindings/clock/clock-bindings.txt - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" - property. Required clocks are "cfg_ahb_clk" and "phy_reset". + property. Required clock is "phy_reset". - phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode. Optional properties: @@ -171,7 +190,13 @@ Optional properties: allows us to manipulate QUSB PHY bits eg. to enable D+ pull-up using s/w control in device mode. The reg-names property is required if the reg property is specified. + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. "cfg_ahb_clk", "ref_clk_src" and "ref_clk" are optional clocks. - qcom,qusb-phy-init-seq: QUSB PHY initialization sequence with value,reg pair. + - qcom,qusb-phy-host-init-seq: QUSB PHY initialization sequence for host mode + with value,reg pair. - qcom,emu-init-seq : emulation initialization sequence with value,reg pair. - qcom,phy-pll-reset-seq : emulation PLL reset sequence with value,reg pair. - qcom,emu-dcm-reset-seq : emulation DCM reset sequence with value,reg pair. diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index 7d323b91f031..2b2bfe428c79 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -10,6 +10,11 @@ Required properties : "hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM. "pwr_event_irq" : Interrupt to controller for asynchronous events in LPM. Used for SS-USB power events. + - clocks: a list of phandles to the controller clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "xo", "iface_clk", "core_clk", "sleep_clk" + and "utmi_clk". Optional properties : - reg: Additional registers @@ -27,6 +32,10 @@ Optional properties : - interrupt-names : Optional interrupt resource entries are: "pmic_id_irq" : Interrupt from PMIC for external ID pin notification. "ss_phy_irq" : Interrupt from super speed phy for wake up notification. + - clocks: a list of phandles to the controller clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Optional clocks are "bus_aggr_clk" and "cfg_ahb_clk". - qcom,charging-disabled: If present then battery charging using USB is disabled. - vbus_dwc3-supply: phandle to the 5V VBUS supply regulator used for host mode. @@ -77,6 +86,18 @@ Example MSM USB3.0 controller device node : qcom,msm_bus,vectors = <61 512 0 0>, <61 512 240000000 960000000>; + + clocks = <&clock_gcc clk_gcc_usb30_master_clk>, + <&clock_gcc clk_gcc_cfg_noc_usb3_axi_clk>, + <&clock_gcc clk_gcc_aggre1_usb3_axi_clk>, + <&clock_gcc clk_gcc_usb30_mock_utmi_clk>, + <&clock_gcc clk_gcc_usb30_sleep_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_gcc clk_cxo_dwc3_clk>; + + clock-names = "core_clk", "iface_clk", "bus_aggr_clk", + "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + dwc3@f9200000 { compatible = "synopsys,dwc3"; reg = <0xf9200000 0xfc000>; diff --git a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi index a2c8c89b08a3..c86351e48d5f 100644 --- a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi @@ -60,6 +60,7 @@ <0xc8ce024 0x4>; reg-names = "base", "hw_ctrl_addr"; qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index fad834199be5..47ba50ab8270 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -339,6 +339,41 @@ channel = <0xc>; }; }; + + pmicobalt_fg: qpnp,fg { + compatible = "qcom,fg-gen3"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmicobalt_revid>; + io-channels = <&pmicobalt_rradc 0>; + io-channel-names = "rradc_batt_id"; + status = "okay"; + + qcom,fg-batt-soc@4000 { + status = "okay"; + reg = <0x4000 0x100>; + interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "soc-update", + "soc-ready", + "bsoc-delta", + "msoc-delta"; + }; + + qcom,fg-batt-info@4100 { + status = "okay"; + reg = <0x4100 0x100>; + interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "batt-missing"; + }; + + qcom,fg-memif@4400 { + status = "okay"; + reg = <0x4400 0x100>; + }; + }; }; qcom,pmicobalt@3 { diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi index 25e0d99987db..07423a601b35 100644 --- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi @@ -79,6 +79,8 @@ qcom,snapshot-size = <1048576>; //bytes + qcom,gpu-qdss-stm = <0x081c0000 0x40000>; // base addr, size + /* Trace bus */ coresight-id = <300>; coresight-name = "coresight-gfx"; diff --git a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi index 55fbca7dc9a1..96279288d336 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi @@ -719,7 +719,6 @@ "SpkrLeft IN", "SPK1 OUT", "SpkrRight IN", "SPK2 OUT"; - qcom,hdmi-audio-rx; asoc-codec = <&stub_codec>, <&hdmi_audio>; asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-audio-codec-rx"; qcom,hph-en1-gpio = <&pmi8994_gpios 10 0>; diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 2da89bd9ac6e..7a26b9f0b227 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -3004,13 +3004,14 @@ qcom,tasha-mclk-clk-freq = <9600000>; asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, - <&pcmnoirq>; + <&afe>, <&lsm>, <&routing>, <&cpe>, + <&compr>, <&pcmnoirq>, <&cpe3>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm", - "msm-compr-dsp", "msm-pcm-dsp-noirq"; + "msm-compr-dsp", "msm-pcm-dsp-noirq", + "msm-cpe-lsm.3"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, @@ -3122,6 +3123,12 @@ cpe: qcom,msm-cpe-lsm { compatible = "qcom,msm-cpe-lsm"; + qcom,msm-cpe-lsm-id = <1>; + }; + + cpe3: qcom,msm-cpe-lsm@3 { + compatible = "qcom,msm-cpe-lsm"; + qcom,msm-cpe-lsm-id = <3>; }; qcom,msm-dai-q6 { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts index aebd9a1440de..c8402ba5b69a 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts @@ -21,3 +21,18 @@ compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp"; qcom,board-id = <1 0>; }; + +&qusb_phy0 { + qcom,qusb-phy-host-init-seq = + /* value reg_offsets> */ + <0x63 0x210 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x8c 0x21c + 0x05 0x23c + 0x03 0x240 + 0xff 0x218 + 0x62 0x210>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi index 96698951bd6c..4afaa3aa51be 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi @@ -20,6 +20,7 @@ reg-names = "tmc-base", "bam-base"; arm,buffer-size = <0x400000>; + arm,sg-enable; coresight-ctis = <&cti0 &cti8>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts index e5708fc8d743..89ef72c104f5 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts @@ -21,3 +21,18 @@ compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp"; qcom,board-id = <8 0>; }; + +&qusb_phy0 { + qcom,qusb-phy-host-init-seq = + /* value reg_offsets> */ + <0x63 0x210 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x8c 0x21c + 0x05 0x23c + 0x03 0x240 + 0xff 0x218 + 0x62 0x210>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi index 1094d96bd100..6db4ed1f45e1 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi @@ -1425,6 +1425,84 @@ }; }; + mdss_dp_aux_active: mdss_dp_aux_active { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; + }; + + mdss_dp_aux_suspend: mdss_dp_aux_suspend { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_usbplug_cc_active: mdss_dp_usbplug_cc_active { + mux { + pins = "gpio38"; + function = "usb_phy"; + }; + + config { + pins = "gpio38"; + bias-disable; + drive-strength = <16>; + }; + }; + + mdss_dp_usbplug_cc_suspend: mdss_dp_usbplug_cc_suspend { + mux { + pins = "gpio38"; + function = "usb_phy"; + }; + + config { + pins = "gpio38"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_hpd_active: mdss_dp_hpd_active { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <16>; + }; + }; + + mdss_dp_hpd_suspend: mdss_dp_hpd_suspend { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <2>; + }; + }; + blsp2_uart3_active: blsp2_uart3_active { mux { pins = "gpio49", "gpio50", "gpio51", "gpio52"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index 5dc530ea8494..f02abd2173ef 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -716,6 +716,7 @@ reg = <0x100000 0xb0000>; reg-names = "cc_base"; vdd_dig-supply = <&pmcobalt_s1_level>; + vdd_dig_ao-supply = <&pmcobalt_s1_level_ao>; #clock-cells = <1>; }; @@ -1726,11 +1727,10 @@ <&clock_gcc clk_gcc_aggre1_usb3_axi_clk>, <&clock_gcc clk_gcc_usb30_mock_utmi_clk>, <&clock_gcc clk_gcc_usb30_sleep_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_cxo_dwc3_clk>; clock-names = "core_clk", "iface_clk", "bus_aggr_clk", - "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + "utmi_clk", "sleep_clk", "xo"; dwc3@a800000 { compatible = "snps,dwc3"; @@ -1778,7 +1778,7 @@ }; qusb_phy0: qusb@c012000 { - compatible = "qcom,qusb2phy"; + compatible = "qcom,qusb2phy-v2"; reg = <0x0c012000 0x2a8>, <0x0a8f8800 0x400>; reg-names = "qusb_phy_base", @@ -1798,11 +1798,9 @@ clocks = <&clock_gcc clk_ln_bb_clk1>, <&clock_gcc clk_gcc_rx1_usb2_clkref_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_gcc_qusb2phy_prim_reset>; - clock-names = "ref_clk_src", "ref_clk", "cfg_ahb_clk", - "phy_reset"; + clock-names = "ref_clk_src", "ref_clk", "phy_reset"; }; ssphy: ssphy@c010000 { @@ -1950,13 +1948,12 @@ clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>, <&clock_gcc clk_gcc_usb3_phy_pipe_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_gcc_usb3_phy_reset>, <&clock_gcc clk_gcc_usb3phy_phy_reset>, <&clock_gcc clk_ln_bb_clk1>, <&clock_gcc clk_gcc_usb3_clkref_clk>; - clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset", + clock-names = "aux_clk", "pipe_clk", "phy_reset", "phy_phy_reset", "ref_clk_src", "ref_clk"; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index ea60ed90cf4f..9ccf55322de1 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -331,6 +331,58 @@ qcom,mpu-enabled; }; + qcom,glink-smem-native-xprt-modem@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x8000>; + interrupts = <0 452 1>; + label = "mpss"; + }; + + qcom,glink-smem-native-xprt-adsp@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x200>; + interrupts = <0 157 1>; + label = "lpass"; + qcom,qos-config = <&glink_qos_adsp>; + qcom,ramp-time = <0xaf>; + }; + + glink_qos_adsp: qcom,glink-qos-config-adsp { + compatible = "qcom,glink-qos-config"; + qcom,flow-info = <0x3c 0x0>, + <0x3c 0x0>, + <0x3c 0x0>, + <0x3c 0x0>; + qcom,mtu-size = <0x800>; + qcom,tput-stats-cycle = <0xa>; + }; + + qcom,glink-smem-native-xprt-cdsp@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x20000000>; + interrupts = <0 513 1>; + label = "cdsp"; + }; + + qcom,glink-smem-native-xprt-rpm@68000 { + compatible = "qcom,glink-rpm-native-xprt"; + reg = <0x778000 0x7000>, + <0x17911008 0x4>; + reg-names = "msgram", "irq-reg-base"; + qcom,irq-mask = <0x1>; + interrupts = <0 168 1>; + label = "rpm"; + }; + glink_mpss: qcom,glink-ssr-modem { compatible = "qcom,glink_ssr"; label = "modem"; @@ -366,6 +418,80 @@ <&glink_rpm>; qcom,xprt = "smem"; }; + + qcom,glink_pkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-loopback_cntl { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl"; + }; + + qcom,glinkpkt-loopback_data { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + }; + + qcom,ipc_router { + compatible = "qcom,ipc_router"; + qcom,node-id = <1>; + }; + + qcom,ipc_router_modem_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "mpss"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + qcom,ipc_router_q6_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "lpass"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + qcom,ipc_router_cdsp_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "cdsp"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; }; #include "msmfalcon-ion.dtsi" diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index f396b0b7f4cc..fa6cdb3d055f 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -85,6 +85,7 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y +CONFIG_INET_ESP=y # CONFIG_INET_LRO is not set CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index be3e4ce1492a..239baffa6cdb 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -57,6 +57,8 @@ CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y CONFIG_SECCOMP=y CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y # CONFIG_EFI is not set CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set @@ -302,6 +304,7 @@ CONFIG_MSM_PM=y CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y +CONFIG_QPNP_FG_GEN3=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y @@ -379,6 +382,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_DP_PANEL=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -467,6 +471,9 @@ CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y CONFIG_MSM_SMD=y @@ -546,6 +553,19 @@ CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 2e9a7908307b..8b9da2d6b0d3 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -307,6 +307,7 @@ CONFIG_MSM_PM=y CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y +CONFIG_QPNP_FG_GEN3=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y @@ -384,6 +385,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_DP_PANEL=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 8fa6f87cc31d..52d046b6a920 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -674,7 +674,9 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = { {.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_init}, {.compatible = "arm,cortex-a53-pmu", .data = armv8_a53_pmu_init}, {.compatible = "arm,cortex-a57-pmu", .data = armv8_a57_pmu_init}, +#ifdef CONFIG_ARCH_MSM8996 {.compatible = "qcom,kryo-pmuv3", .data = kryo_pmu_init}, +#endif {}, }; diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig index 3aab117bd553..f9ecc8270f54 100644 --- a/arch/um/configs/x86_64_defconfig +++ b/arch/um/configs/x86_64_defconfig @@ -15,7 +15,6 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y -CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_PID_NS is not set @@ -54,6 +53,7 @@ CONFIG_UNIX=y CONFIG_INET=y # CONFIG_INET_LRO is not set # CONFIG_IPV6 is not set +# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_UML_NET=y CONFIG_UML_NET_ETHERTAP=y CONFIG_UML_NET_TUNTAP=y diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c index 1524d3d8ed46..7299863ff42b 100644 --- a/drivers/clk/msm/clock-gcc-cobalt.c +++ b/drivers/clk/msm/clock-gcc-cobalt.c @@ -56,6 +56,7 @@ static void __iomem *virt_dbgbase; } static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); +static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL); DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_clk_src_ao, RPM_MISC_CLK_TYPE, CXO_CLK_SRC_ID, 19200000); @@ -211,7 +212,7 @@ static struct rcg_clk hmss_ahb_clk_src = { .c = { .dbg_name = "hmss_ahb_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP3(LOWER, 19200000, LOW, 50000000, + VDD_DIG_FMAX_MAP3_AO(LOWER, 19200000, LOW, 50000000, NOMINAL, 100000000), CLK_INIT(hmss_ahb_clk_src.c), }, @@ -1029,7 +1030,7 @@ static struct rcg_clk hmss_gpll0_clk_src = { .c = { .dbg_name = "hmss_gpll0_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP1(LOWER, 600000000), + VDD_DIG_FMAX_MAP1_AO(LOWER, 600000000), CLK_INIT(hmss_gpll0_clk_src.c), }, }; @@ -2184,17 +2185,6 @@ static struct reset_clk gcc_qusb2phy_sec_reset = { }, }; -static struct branch_clk gcc_usb_phy_cfg_ahb2phy_clk = { - .cbcr_reg = GCC_USB_PHY_CFG_AHB2PHY_CBCR, - .has_sibling = 1, - .base = &virt_base, - .c = { - .dbg_name = "gcc_usb_phy_cfg_ahb2phy_clk", - .ops = &clk_ops_branch, - CLK_INIT(gcc_usb_phy_cfg_ahb2phy_clk.c), - }, -}; - static struct branch_clk gcc_wcss_ahb_s0_clk = { .cbcr_reg = GCC_WCSS_AHB_S0_CBCR, .has_sibling = 1, @@ -2380,7 +2370,6 @@ static struct mux_clk gcc_debug_mux = { { &gcc_usb30_mock_utmi_clk.c, 0x0040 }, { &gcc_usb3_phy_aux_clk.c, 0x0041 }, { &gcc_usb3_phy_pipe_clk.c, 0x0042 }, - { &gcc_usb_phy_cfg_ahb2phy_clk.c, 0x0045 }, { &gcc_sdcc2_apps_clk.c, 0x0046 }, { &gcc_sdcc2_ahb_clk.c, 0x0047 }, { &gcc_sdcc4_apps_clk.c, 0x0048 }, @@ -2687,7 +2676,6 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = { CLK_LIST(gcc_usb30_sleep_clk), CLK_LIST(gcc_usb3_phy_aux_clk), CLK_LIST(gcc_usb3_phy_pipe_clk), - CLK_LIST(gcc_usb_phy_cfg_ahb2phy_clk), CLK_LIST(gcc_prng_ahb_clk), CLK_LIST(gcc_boot_rom_ahb_clk), CLK_LIST(gcc_wcss_ahb_s0_clk), @@ -2748,11 +2736,6 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev) return -ENOMEM; } - /* Set the HMSS_AHB_CLK_ENA bit to enable the hmss_ahb_clk */ - regval = readl_relaxed(virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE); - regval |= BIT(21); - writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE); - /* * Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be * turned off by hardware during certain apps low power modes. @@ -2769,6 +2752,14 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev) return PTR_ERR(vdd_dig.regulator[0]); } + vdd_dig_ao.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_ao"); + if (IS_ERR(vdd_dig_ao.regulator[0])) { + if (!(PTR_ERR(vdd_dig_ao.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_dig_ao regulator\n"); + return PTR_ERR(vdd_dig_ao.regulator[0]); + } + bimc_clk.c.parent = &cxo_clk_src.c; ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm_cobalt, ARRAY_SIZE(msm_clocks_rpm_cobalt)); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c index eac501e28d7b..d5d55a58bf7f 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c @@ -82,7 +82,7 @@ #define HDMI_HZ_TO_MHZ 1000000 #define HDMI_REF_CLOCK_MHZ 19.2 #define HDMI_REF_CLOCK_HZ (HDMI_REF_CLOCK_MHZ * 1000000) -#define HDMI_VCO_MIN_RATE_HZ 30000000 +#define HDMI_VCO_MIN_RATE_HZ 25000000 #define HDMI_VCO_MAX_RATE_HZ 600000000 struct cobalt_reg_cfg { diff --git a/drivers/clk/msm/vdd-level-cobalt.h b/drivers/clk/msm/vdd-level-cobalt.h index 2cb40afafe3f..f847a4104d4d 100644 --- a/drivers/clk/msm/vdd-level-cobalt.h +++ b/drivers/clk/msm/vdd-level-cobalt.h @@ -50,11 +50,19 @@ }, \ .num_fmax = VDD_DIG_NUM -#define VDD_DIG_FMAX_MAP2_AO(l1, f1, l2, f2) \ +#define VDD_DIG_FMAX_MAP1_AO(l1, f1) \ + .vdd_class = &vdd_dig_ao, \ + .fmax = (unsigned long[VDD_DIG_NUM]) { \ + [VDD_DIG_##l1] = (f1), \ + }, \ + .num_fmax = VDD_DIG_NUM + +#define VDD_DIG_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \ .vdd_class = &vdd_dig_ao, \ .fmax = (unsigned long[VDD_DIG_NUM]) { \ [VDD_DIG_##l1] = (f1), \ [VDD_DIG_##l2] = (f2), \ + [VDD_DIG_##l3] = (f3), \ }, \ .num_fmax = VDD_DIG_NUM diff --git a/drivers/clk/qcom/mdss/Kconfig b/drivers/clk/qcom/mdss/Kconfig new file mode 100644 index 000000000000..229780e45bb8 --- /dev/null +++ b/drivers/clk/qcom/mdss/Kconfig @@ -0,0 +1,6 @@ +config MSM_MDSS_PLL + bool "MDSS pll programming" + ---help--- + It provides support for DSI, eDP and HDMI interface pll programming on MDSS + hardware. It also handles the pll specific resources and turn them on/off when + mdss pll client tries to enable/disable pll clocks. diff --git a/drivers/clk/qcom/mdss/Makefile b/drivers/clk/qcom/mdss/Makefile new file mode 100644 index 000000000000..75891dc10dda --- /dev/null +++ b/drivers/clk/qcom/mdss/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c new file mode 100644 index 000000000000..6d2694d5a2e9 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c @@ -0,0 +1,1137 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/delay.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-dsi-pll-8996.h" + +#define DSI_PLL_POLL_MAX_READS 15 +#define DSI_PLL_POLL_TIMEOUT_US 1000 +#define MSM8996_DSI_PLL_REVISION_2 2 + +#define CEIL(x, y) (((x) + ((y)-1)) / (y)) + +int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel) +{ + return 0; +} + +int get_mdss_byte_mux_sel_8996(struct mux_clk *clk) +{ + return 0; +} + +int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel) +{ + return 0; +} + +int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk) +{ + return 0; +} + +static int mdss_pll_read_stored_trim_codes( + struct mdss_pll_resources *dsi_pll_res, s64 vco_clk_rate) +{ + int i; + int rc = 0; + bool found = false; + + if (!dsi_pll_res->dfps) { + rc = -EINVAL; + goto end_read; + } + + for (i = 0; i < dsi_pll_res->dfps->panel_dfps.frame_rate_cnt; i++) { + struct dfps_codes_info *codes_info = + &dsi_pll_res->dfps->codes_dfps[i]; + + pr_debug("valid=%d frame_rate=%d, vco_rate=%d, code %d %d\n", + codes_info->is_valid, codes_info->frame_rate, + codes_info->clk_rate, codes_info->pll_codes.pll_codes_1, + codes_info->pll_codes.pll_codes_2); + + if (vco_clk_rate != codes_info->clk_rate && + codes_info->is_valid) + continue; + + dsi_pll_res->cache_pll_trim_codes[0] = + codes_info->pll_codes.pll_codes_1; + dsi_pll_res->cache_pll_trim_codes[1] = + codes_info->pll_codes.pll_codes_2; + found = true; + break; + } + + if (!found) { + rc = -EINVAL; + goto end_read; + } + + pr_debug("core_kvco_code=0x%x core_vco_tune=0x%x\n", + dsi_pll_res->cache_pll_trim_codes[0], + dsi_pll_res->cache_pll_trim_codes[1]); + +end_read: + return rc; +} + +int post_n1_div_set_div(struct div_clk *clk, int div) +{ + struct mdss_pll_resources *pll = clk->priv; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + int rc; + u32 n1div = 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + pdb = (struct dsi_pll_db *)pll->priv; + pout = &pdb->out; + + /* + * vco rate = bit_clk * postdiv * n1div + * vco range from 1300 to 2600 Mhz + * postdiv = 1 + * n1div = 1 to 15 + * n1div = roundup(1300Mhz / bit_clk) + * support bit_clk above 86.67Mhz + */ + + /* this is for vco/bit clock */ + pout->pll_postdiv = 1; /* fixed, divided by 1 */ + pout->pll_n1div = div; + + n1div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n1div &= ~0xf; + n1div |= (div & 0xf); + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n1div); + /* ensure n1 divider is programed */ + wmb(); + pr_debug("ndx=%d div=%d postdiv=%x n1div=%x\n", + pll->index, div, pout->pll_postdiv, pout->pll_n1div); + + mdss_pll_resource_enable(pll, false); + + return 0; +} + +int post_n1_div_get_div(struct div_clk *clk) +{ + u32 div; + int rc; + struct mdss_pll_resources *pll = clk->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + /* + * postdiv = 1/2/4/8 + * n1div = 1 - 15 + * fot the time being, assume postdiv = 1 + */ + + div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + div &= 0xF; + pr_debug("n1 div = %d\n", div); + + mdss_pll_resource_enable(pll, false); + + return div; +} + +int n2_div_set_div(struct div_clk *clk, int div) +{ + int rc; + u32 n2div; + struct mdss_pll_resources *pll = clk->priv; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + struct mdss_pll_resources *slave; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + pdb = (struct dsi_pll_db *)pll->priv; + pout = &pdb->out; + + /* this is for pixel clock */ + n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n2div &= ~0xf0; /* bits 4 to 7 */ + n2div |= (div << 4); + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n2div); + + /* commit slave if split display is enabled */ + slave = pll->slave; + if (slave) + MDSS_PLL_REG_W(slave->pll_base, DSIPHY_CMN_CLK_CFG0, n2div); + + pout->pll_n2div = div; + + /* set dsiclk_sel=1 so that n2div *= 2 */ + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG1, 1); + pr_debug("ndx=%d div=%d n2div=%x\n", pll->index, div, n2div); + + mdss_pll_resource_enable(pll, false); + + return rc; +} + +int shadow_n2_div_set_div(struct div_clk *clk, int div) +{ + struct mdss_pll_resources *pll = clk->priv; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + u32 data; + + pdb = pll->priv; + pout = &pdb->out; + + pout->pll_n2div = div; + + data = (pout->pll_n1div | (pout->pll_n2div << 4)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL19, + DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_CLK_CFG1, + data, 1); + return 0; +} + +int n2_div_get_div(struct div_clk *clk) +{ + int rc; + u32 n2div; + struct mdss_pll_resources *pll = clk->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d resources\n", + pll->index); + return rc; + } + + n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n2div >>= 4; + n2div &= 0x0f; + + mdss_pll_resource_enable(pll, false); + + pr_debug("ndx=%d div=%d\n", pll->index, n2div); + + return n2div; +} + +static bool pll_is_pll_locked_8996(struct mdss_pll_resources *pll) +{ + u32 status; + bool pll_locked; + + /* poll for PLL ready status */ + if (readl_poll_timeout_atomic((pll->pll_base + + DSIPHY_PLL_RESET_SM_READY_STATUS), + status, + ((status & BIT(5)) > 0), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_err("DSI PLL ndx=%d status=%x failed to Lock\n", + pll->index, status); + pll_locked = false; + } else if (readl_poll_timeout_atomic((pll->pll_base + + DSIPHY_PLL_RESET_SM_READY_STATUS), + status, + ((status & BIT(0)) > 0), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_err("DSI PLL ndx=%d status=%x PLl not ready\n", + pll->index, status); + pll_locked = false; + } else { + pll_locked = true; + } + + return pll_locked; +} + +static void dsi_pll_start_8996(void __iomem *pll_base) +{ + pr_debug("start PLL at base=%p\n", pll_base); + + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VREF_CFG1, 0x10); + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 1); +} + +static void dsi_pll_stop_8996(void __iomem *pll_base) +{ + pr_debug("stop PLL at base=%p\n", pll_base); + + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0); +} + +int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll) +{ + int rc = 0; + + if (!pll) { + pr_err("Invalid PLL resources\n"); + return -EINVAL; + } + + dsi_pll_start_8996(pll->pll_base); + + /* + * both DSIPHY_PLL_CLKBUFLR_EN and DSIPHY_CMN_GLBL_TEST_CTRL + * enabled at mdss_dsi_8996_phy_config() + */ + + if (!pll_is_pll_locked_8996(pll)) { + pr_err("DSI PLL ndx=%d lock failed\n", pll->index); + rc = -EINVAL; + goto init_lock_err; + } + + pr_debug("DSI PLL ndx=%d Lock success\n", pll->index); + +init_lock_err: + return rc; +} + +static int dsi_pll_enable(struct clk *c) +{ + int i, rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < vco->pll_en_seq_cnt; i++) { + rc = vco->pll_enable_seqs[i](pll); + pr_debug("DSI PLL %s after sequence #%d\n", + rc ? "unlocked" : "locked", i + 1); + if (!rc) + break; + } + + if (rc) + pr_err("ndx=%d DSI PLL failed to lock\n", pll->index); + else + pll->pll_on = true; + + return rc; +} + +static void dsi_pll_disable(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct mdss_pll_resources *slave; + + if (!pll->pll_on && + mdss_pll_resource_enable(pll, true)) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return; + } + + pll->handoff_resources = false; + slave = pll->slave; + + dsi_pll_stop_8996(pll->pll_base); + + mdss_pll_resource_enable(pll, false); + + pll->pll_on = false; + + pr_debug("DSI PLL ndx=%d Disabled\n", pll->index); +} + +static void mdss_dsi_pll_8996_input_init(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + pdb->in.fref = 19200000; /* 19.2 Mhz*/ + pdb->in.fdata = 0; /* bit clock rate */ + pdb->in.dsiclk_sel = 1; /* 1, reg: 0x0014 */ + pdb->in.ssc_en = pll->ssc_en; /* 1, reg: 0x0494, bit 0 */ + pdb->in.ldo_en = 0; /* 0, reg: 0x004c, bit 0 */ + + /* fixed input */ + pdb->in.refclk_dbler_en = 0; /* 0, reg: 0x04c0, bit 1 */ + pdb->in.vco_measure_time = 5; /* 5, unknown */ + pdb->in.kvco_measure_time = 5; /* 5, unknown */ + pdb->in.bandgap_timer = 4; /* 4, reg: 0x0430, bit 3 - 5 */ + pdb->in.pll_wakeup_timer = 5; /* 5, reg: 0x043c, bit 0 - 2 */ + pdb->in.plllock_cnt = 1; /* 1, reg: 0x0488, bit 1 - 2 */ + pdb->in.plllock_rng = 0; /* 0, reg: 0x0488, bit 3 - 4 */ + pdb->in.ssc_center = pll->ssc_center;/* 0, reg: 0x0494, bit 1 */ + pdb->in.ssc_adj_period = 37; /* 37, reg: 0x498, bit 0 - 9 */ + pdb->in.ssc_spread = pll->ssc_ppm / 1000; + pdb->in.ssc_freq = pll->ssc_freq; + + pdb->in.pll_ie_trim = 4; /* 4, reg: 0x0400 */ + pdb->in.pll_ip_trim = 4; /* 4, reg: 0x0404 */ + pdb->in.pll_cpcset_cur = 1; /* 1, reg: 0x04f0, bit 0 - 2 */ + pdb->in.pll_cpmset_cur = 1; /* 1, reg: 0x04f0, bit 3 - 5 */ + pdb->in.pll_icpmset = 4; /* 4, reg: 0x04fc, bit 3 - 5 */ + pdb->in.pll_icpcset = 4; /* 4, reg: 0x04fc, bit 0 - 2 */ + pdb->in.pll_icpmset_p = 0; /* 0, reg: 0x04f4, bit 0 - 2 */ + pdb->in.pll_icpmset_m = 0; /* 0, reg: 0x04f4, bit 3 - 5 */ + pdb->in.pll_icpcset_p = 0; /* 0, reg: 0x04f8, bit 0 - 2 */ + pdb->in.pll_icpcset_m = 0; /* 0, reg: 0x04f8, bit 3 - 5 */ + pdb->in.pll_lpf_res1 = 3; /* 3, reg: 0x0504, bit 0 - 3 */ + pdb->in.pll_lpf_cap1 = 11; /* 11, reg: 0x0500, bit 0 - 3 */ + pdb->in.pll_lpf_cap2 = 1; /* 1, reg: 0x0500, bit 4 - 7 */ + pdb->in.pll_iptat_trim = 7; + pdb->in.pll_c3ctrl = 2; /* 2 */ + pdb->in.pll_r3ctrl = 1; /* 1 */ +} + +static void pll_8996_ssc_calc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + u32 period, ssc_period; + u32 ref, rem; + s64 step_size; + + pr_debug("%s: vco=%lld ref=%lld\n", __func__, + pll->vco_current_rate, pll->vco_ref_clk_rate); + + ssc_period = pdb->in.ssc_freq / 500; + period = (unsigned long)pll->vco_ref_clk_rate / 1000; + ssc_period = CEIL(period, ssc_period); + ssc_period -= 1; + pdb->out.ssc_period = ssc_period; + + pr_debug("%s: ssc, freq=%d spread=%d period=%d\n", __func__, + pdb->in.ssc_freq, pdb->in.ssc_spread, pdb->out.ssc_period); + + step_size = (u32)pll->vco_current_rate; + ref = pll->vco_ref_clk_rate; + ref /= 1000; + step_size = div_s64(step_size, ref); + step_size <<= 20; + step_size = div_s64(step_size, 1000); + step_size *= pdb->in.ssc_spread; + step_size = div_s64(step_size, 1000); + step_size *= (pdb->in.ssc_adj_period + 1); + + rem = 0; + step_size = div_s64_rem(step_size, ssc_period + 1, &rem); + if (rem) + step_size++; + + pr_debug("%s: step_size=%lld\n", __func__, step_size); + + step_size &= 0x0ffff; /* take lower 16 bits */ + + pdb->out.ssc_step_size = step_size; +} + +static void pll_8996_dec_frac_calc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + s64 multiplier = BIT(20); + s64 dec_start_multiple, dec_start, pll_comp_val; + s32 duration, div_frac_start; + s64 vco_clk_rate = pll->vco_current_rate; + s64 fref = pll->vco_ref_clk_rate; + + pr_debug("vco_clk_rate=%lld ref_clk_rate=%lld\n", + vco_clk_rate, fref); + + dec_start_multiple = div_s64(vco_clk_rate * multiplier, fref); + div_s64_rem(dec_start_multiple, multiplier, &div_frac_start); + + dec_start = div_s64(dec_start_multiple, multiplier); + + pout->dec_start = (u32)dec_start; + pout->div_frac_start = div_frac_start; + + if (pin->plllock_cnt == 0) + duration = 1024; + else if (pin->plllock_cnt == 1) + duration = 256; + else if (pin->plllock_cnt == 2) + duration = 128; + else + duration = 32; + + pll_comp_val = duration * dec_start_multiple; + pll_comp_val = div_s64(pll_comp_val, multiplier); + do_div(pll_comp_val, 10); + + pout->plllock_cmp = (u32)pll_comp_val; + + pout->pll_txclk_en = 1; + if (pll->revision == MSM8996_DSI_PLL_REVISION_2) + pout->cmn_ldo_cntrl = 0x3c; + else + pout->cmn_ldo_cntrl = 0x1c; +} + +static u32 pll_8996_kvco_slop(u32 vrate) +{ + u32 slop = 0; + + if (vrate > 1300000000UL && vrate <= 1800000000UL) + slop = 600; + else if (vrate > 1800000000UL && vrate < 2300000000UL) + slop = 400; + else if (vrate > 2300000000UL && vrate < 2600000000UL) + slop = 280; + + return slop; +} + +static void pll_8996_calc_vco_count(struct dsi_pll_db *pdb, + s64 vco_clk_rate, s64 fref) +{ + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + s64 data; + u32 cnt; + + data = fref * pin->vco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 2; + pout->pll_vco_div_ref = data; + + data = (unsigned long)vco_clk_rate / 1000000; /* unit is Mhz */ + data *= pin->vco_measure_time; + do_div(data, 10); + pout->pll_vco_count = data; /* reg: 0x0474, 0x0478 */ + + data = fref * pin->kvco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 1; + pout->pll_kvco_div_ref = data; + + cnt = pll_8996_kvco_slop(vco_clk_rate); + cnt *= 2; + do_div(cnt, 100); + cnt *= pin->kvco_measure_time; + pout->pll_kvco_count = cnt; + + pout->pll_misc1 = 16; + pout->pll_resetsm_cntrl = 48; + pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3; + pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer; + pout->pll_kvco_code = 0; +} + +static void pll_db_commit_ssc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + data = pin->ssc_adj_period; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER1, data); + data = (pin->ssc_adj_period >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER2, data); + + data = pout->ssc_period; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER1, data); + data = (pout->ssc_period >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER2, data); + + data = pout->ssc_step_size; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE1, data); + data = (pout->ssc_step_size >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE2, data); + + data = (pin->ssc_center & 0x01); + data <<= 1; + data |= 0x01; /* enable */ + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_EN_CENTER, data); + + wmb(); /* make sure register committed */ +} + +static void pll_db_commit_common(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + /* confgiure the non frequency dependent pll registers */ + data = 0; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SYSCLK_EN_RESET, data); + + /* DSIPHY_PLL_CLKBUFLR_EN updated at dsi phy */ + + data = pout->pll_txclk_en; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_TXCLK_EN, data); + + data = pout->pll_resetsm_cntrl; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL, data); + data = pout->pll_resetsm_cntrl2; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL2, data); + data = pout->pll_resetsm_cntrl5; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL5, data); + + data = pout->pll_vco_div_ref; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF1, data); + data = (pout->pll_vco_div_ref >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF2, data); + + data = pout->pll_kvco_div_ref; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF1, data); + data = (pout->pll_kvco_div_ref >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF2, data); + + data = pout->pll_misc1; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_MISC1, data); + + data = pin->pll_ie_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IE_TRIM, data); + + data = pin->pll_ip_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IP_TRIM, data); + + data = ((pin->pll_cpmset_cur << 3) | pin->pll_cpcset_cur); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CP_SET_CUR, data); + + data = ((pin->pll_icpcset_p << 3) | pin->pll_icpcset_m); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPCSET, data); + + data = ((pin->pll_icpmset_p << 3) | pin->pll_icpcset_m); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPMSET, data); + + data = ((pin->pll_icpmset << 3) | pin->pll_icpcset); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICP_SET, data); + + data = ((pdb->in.pll_lpf_cap2 << 4) | pdb->in.pll_lpf_cap1); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF1, data); + + data = pin->pll_iptat_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IPTAT_TRIM, data); + + data = (pdb->in.pll_c3ctrl | (pdb->in.pll_r3ctrl << 4)); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_CRCTRL, data); +} + +static void pll_db_commit_8996(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + data = pout->cmn_ldo_cntrl; + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_LDO_CNTRL, data); + + pll_db_commit_common(pll, pdb); + + /* de assert pll start and apply pll sw reset */ + /* stop pll */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0); + + /* pll sw reset */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0x20); + wmb(); /* make sure register committed */ + udelay(10); + + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0); + wmb(); /* make sure register committed */ + + data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data); + + data = 0xff; /* data, clk, pll normal operation */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data); + + /* confgiure the frequency dependent pll registers */ + data = pout->dec_start; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DEC_START, data); + + data = pout->div_frac_start; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START1, data); + data = (pout->div_frac_start >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START2, data); + data = (pout->div_frac_start >> 16); + data &= 0x0f; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START3, data); + + data = pout->plllock_cmp; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP1, data); + data = (pout->plllock_cmp >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP2, data); + data = (pout->plllock_cmp >> 16); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP3, data); + + data = ((pin->plllock_cnt << 1) | (pin->plllock_rng << 3)); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP_EN, data); + + data = pout->pll_vco_count; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT1, data); + data = (pout->pll_vco_count >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT2, data); + + data = pout->pll_kvco_count; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT1, data); + data = (pout->pll_kvco_count >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT2, data); + + /* + * tx_band = pll_postdiv + * 0: divided by 1 <== for now + * 1: divided by 2 + * 2: divided by 4 + * 3: divided by 8 + */ + data = (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF2_POSTDIV, data); + + data = (pout->pll_n1div | (pout->pll_n2div << 4)); + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG0, data); + + if (pll->ssc_en) + pll_db_commit_ssc(pll, pdb); + + wmb(); /* make sure register committed */ +} + +/* + * pll_source_finding: + * Both GLBL_TEST_CTRL and CLKBUFLR_EN are configured + * at mdss_dsi_8996_phy_config() + */ +static int pll_source_finding(struct mdss_pll_resources *pll) +{ + u32 clk_buf_en; + u32 glbl_test_ctrl; + + glbl_test_ctrl = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_CMN_GLBL_TEST_CTRL); + clk_buf_en = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_CLKBUFLR_EN); + + glbl_test_ctrl &= BIT(2); + glbl_test_ctrl >>= 2; + + pr_debug("%s: pll=%d clk_buf_en=%x glbl_test_ctrl=%x\n", + __func__, pll->index, clk_buf_en, glbl_test_ctrl); + + clk_buf_en &= (PLL_OUTPUT_RIGHT | PLL_OUTPUT_LEFT); + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) && + (clk_buf_en == PLL_OUTPUT_BOTH)) + return PLL_MASTER; + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_RIGHT) && + (clk_buf_en == PLL_OUTPUT_NONE)) + return PLL_SLAVE; + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) && + (clk_buf_en == PLL_OUTPUT_RIGHT)) + return PLL_STANDALONE; + + pr_debug("%s: Error pll setup, clk_buf_en=%x glbl_test_ctrl=%x\n", + __func__, clk_buf_en, glbl_test_ctrl); + + return PLL_UNKNOWN; +} + +static void pll_source_setup(struct mdss_pll_resources *pll) +{ + int status; + struct dsi_pll_db *pdb = (struct dsi_pll_db *)pll->priv; + struct mdss_pll_resources *other; + + if (pdb->source_setup_done) + return; + + pdb->source_setup_done++; + + status = pll_source_finding(pll); + + if (status == PLL_STANDALONE || status == PLL_UNKNOWN) + return; + + other = pdb->next->pll; + if (!other) + return; + + pr_debug("%s: status=%d pll=%d other=%d\n", __func__, + status, pll->index, other->index); + + if (status == PLL_MASTER) + pll->slave = other; + else + other->slave = pll; +} + +int pll_vco_set_rate_8996(struct clk *c, unsigned long rate) +{ + int rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct mdss_pll_resources *slave; + struct dsi_pll_db *pdb; + + pdb = (struct dsi_pll_db *)pll->priv; + if (!pdb) { + pr_err("No prov found\n"); + return -EINVAL; + } + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi plla=%d\n", pll->index); + return rc; + } + + pll_source_setup(pll); + + pr_debug("%s: ndx=%d base=%p rate=%lu slave=%p\n", __func__, + pll->index, pll->pll_base, rate, pll->slave); + + pll->vco_current_rate = rate; + pll->vco_ref_clk_rate = vco->ref_clk_rate; + + mdss_dsi_pll_8996_input_init(pll, pdb); + + pll_8996_dec_frac_calc(pll, pdb); + + if (pll->ssc_en) + pll_8996_ssc_calc(pll, pdb); + + pll_8996_calc_vco_count(pdb, pll->vco_current_rate, + pll->vco_ref_clk_rate); + + /* commit slave if split display is enabled */ + slave = pll->slave; + if (slave) + pll_db_commit_8996(slave, pdb); + + /* commit master itself */ + pll_db_commit_8996(pll, pdb); + + mdss_pll_resource_enable(pll, false); + + return rc; +} + +static void shadow_pll_dynamic_refresh_8996(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + struct dsi_pll_output *pout = &pdb->out; + + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL20, + DSIPHY_CMN_CTRL_0, DSIPHY_PLL_SYSCLK_EN_RESET, + 0xFF, 0x0); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL21, + DSIPHY_PLL_DEC_START, DSIPHY_PLL_DIV_FRAC_START1, + pout->dec_start, (pout->div_frac_start & 0x0FF)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL22, + DSIPHY_PLL_DIV_FRAC_START2, DSIPHY_PLL_DIV_FRAC_START3, + ((pout->div_frac_start >> 8) & 0x0FF), + ((pout->div_frac_start >> 16) & 0x0F)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL23, + DSIPHY_PLL_PLLLOCK_CMP1, DSIPHY_PLL_PLLLOCK_CMP2, + (pout->plllock_cmp & 0x0FF), + ((pout->plllock_cmp >> 8) & 0x0FF)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL24, + DSIPHY_PLL_PLLLOCK_CMP3, DSIPHY_PLL_PLL_VCO_TUNE, + ((pout->plllock_cmp >> 16) & 0x03), + (pll->cache_pll_trim_codes[1] | BIT(7))); /* VCO tune*/ + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL25, + DSIPHY_PLL_KVCO_CODE, DSIPHY_PLL_RESETSM_CNTRL, + (pll->cache_pll_trim_codes[0] | BIT(5)), 0x38); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL26, + DSIPHY_PLL_PLL_LPF2_POSTDIV, DSIPHY_CMN_PLL_CNTRL, + (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1), 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL27, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL28, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL29, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR, 0x0000001E); + MDSS_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2, 0x001FFE00); + + /* + * Ensure all the dynamic refresh registers are written before + * dynamic refresh to change the fps is triggered + */ + wmb(); +} + +int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate) +{ + int rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct dsi_pll_db *pdb; + s64 vco_clk_rate = (s64)rate; + + if (!pll) { + pr_err("PLL data not found\n"); + return -EINVAL; + } + + pdb = pll->priv; + if (!pdb) { + pr_err("No priv data found\n"); + return -EINVAL; + } + + rc = mdss_pll_read_stored_trim_codes(pll, vco_clk_rate); + if (rc) { + pr_err("cannot find pll codes rate=%lld\n", vco_clk_rate); + return -EINVAL; + } + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi plla=%d\n", pll->index); + return rc; + } + + pr_debug("%s: ndx=%d base=%p rate=%lu\n", __func__, + pll->index, pll->pll_base, rate); + + pll->vco_current_rate = rate; + pll->vco_ref_clk_rate = vco->ref_clk_rate; + + mdss_dsi_pll_8996_input_init(pll, pdb); + + pll_8996_dec_frac_calc(pll, pdb); + + pll_8996_calc_vco_count(pdb, pll->vco_current_rate, + pll->vco_ref_clk_rate); + + shadow_pll_dynamic_refresh_8996(pll, pdb); + + rc = mdss_pll_resource_enable(pll, false); + if (rc) { + pr_err("Failed to enable mdss dsi plla=%d\n", pll->index); + return rc; + } + + return rc; +} + +unsigned long pll_vco_get_rate_8996(struct clk *c) +{ + u64 vco_rate, multiplier = BIT(20); + s32 div_frac_start; + u32 dec_start; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + u64 ref_clk = vco->ref_clk_rate; + int rc; + struct mdss_pll_resources *pll = vco->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return rc; + } + + dec_start = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DEC_START); + dec_start &= 0x0ff; + pr_debug("dec_start = 0x%x\n", dec_start); + + div_frac_start = (MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START3) & 0x0f) << 16; + div_frac_start |= (MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START2) & 0x0ff) << 8; + div_frac_start |= MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START1) & 0x0ff; + pr_debug("div_frac_start = 0x%x\n", div_frac_start); + + vco_rate = ref_clk * dec_start; + vco_rate += ((ref_clk * div_frac_start) / multiplier); + + pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); + + mdss_pll_resource_enable(pll, false); + + return (unsigned long)vco_rate; +} + +long pll_vco_round_rate_8996(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + u32 div; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + + div = vco->min_rate / rate; + if (div > 15) { + /* rate < 86.67 Mhz */ + pr_err("rate=%lu NOT supportted\n", rate); + return -EINVAL; + } + + if (rate < vco->min_rate) + rrate = vco->min_rate; + if (rate > vco->max_rate) + rrate = vco->max_rate; + + return rrate; +} + +enum handoff pll_vco_handoff_8996(struct clk *c) +{ + int rc; + enum handoff ret = HANDOFF_DISABLED_CLK; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (is_gdsc_disabled(pll)) + return HANDOFF_DISABLED_CLK; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return ret; + } + + if (pll_is_pll_locked_8996(pll)) { + pll->handoff_resources = true; + pll->pll_on = true; + c->rate = pll_vco_get_rate_8996(c); + ret = HANDOFF_ENABLED_CLK; + } else { + mdss_pll_resource_enable(pll, false); + } + + return ret; +} + +enum handoff shadow_pll_vco_handoff_8996(struct clk *c) +{ + return HANDOFF_DISABLED_CLK; +} + +int pll_vco_prepare_8996(struct clk *c) +{ + int rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (!pll) { + pr_err("Dsi pll resources are not available\n"); + return -EINVAL; + } + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("ndx=%d Failed to enable mdss dsi pll resources\n", + pll->index); + return rc; + } + + if ((pll->vco_cached_rate != 0) + && (pll->vco_cached_rate == c->rate)) { + rc = c->ops->set_rate(c, pll->vco_cached_rate); + if (rc) { + pr_err("index=%d vco_set_rate failed. rc=%d\n", + rc, pll->index); + mdss_pll_resource_enable(pll, false); + goto error; + } + } + + rc = dsi_pll_enable(c); + + if (rc) { + mdss_pll_resource_enable(pll, false); + pr_err("ndx=%d failed to enable dsi pll\n", pll->index); + } + +error: + return rc; +} + +void pll_vco_unprepare_8996(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (!pll) { + pr_err("Dsi pll resources are not available\n"); + return; + } + + pll->vco_cached_rate = c->rate; + dsi_pll_disable(c); +} diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c new file mode 100644 index 000000000000..1de1b997a041 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c @@ -0,0 +1,566 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/workqueue.h> +#include <linux/clk/msm-clock-generic.h> +#include <dt-bindings/clock/msm-clocks-8996.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-dsi-pll-8996.h" + +#define VCO_DELAY_USEC 1 + +static struct dsi_pll_db pll_db[DSI_PLL_NUM]; + +static struct clk_ops n2_clk_src_ops; +static struct clk_ops shadow_n2_clk_src_ops; +static struct clk_ops byte_clk_src_ops; +static struct clk_ops post_n1_div_clk_src_ops; +static struct clk_ops shadow_post_n1_div_clk_src_ops; + +static struct clk_ops clk_ops_gen_mux_dsi; + +/* Op structures */ +static struct clk_ops clk_ops_dsi_vco = { + .set_rate = pll_vco_set_rate_8996, + .round_rate = pll_vco_round_rate_8996, + .handoff = pll_vco_handoff_8996, + .prepare = pll_vco_prepare_8996, + .unprepare = pll_vco_unprepare_8996, +}; + +static struct clk_div_ops post_n1_div_ops = { + .set_div = post_n1_div_set_div, + .get_div = post_n1_div_get_div, +}; + +static struct clk_div_ops n2_div_ops = { /* hr_oclk3 */ + .set_div = n2_div_set_div, + .get_div = n2_div_get_div, +}; + +static struct clk_mux_ops mdss_byte_mux_ops = { + .set_mux_sel = set_mdss_byte_mux_sel_8996, + .get_mux_sel = get_mdss_byte_mux_sel_8996, +}; + +static struct clk_mux_ops mdss_pixel_mux_ops = { + .set_mux_sel = set_mdss_pixel_mux_sel_8996, + .get_mux_sel = get_mdss_pixel_mux_sel_8996, +}; + +/* Shadow ops for dynamic refresh */ +static struct clk_ops clk_ops_shadow_dsi_vco = { + .set_rate = shadow_pll_vco_set_rate_8996, + .round_rate = pll_vco_round_rate_8996, + .handoff = shadow_pll_vco_handoff_8996, +}; + +static struct clk_div_ops shadow_post_n1_div_ops = { + .set_div = post_n1_div_set_div, +}; + +static struct clk_div_ops shadow_n2_div_ops = { + .set_div = shadow_n2_div_set_div, +}; + +static struct dsi_pll_vco_clk dsi0pll_vco_clk = { + .ref_clk_rate = 19200000UL, + .min_rate = 1300000000UL, + .max_rate = 2600000000UL, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi0pll_vco_clk_8996", + .ops = &clk_ops_dsi_vco, + CLK_INIT(dsi0pll_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = { + .ref_clk_rate = 19200000u, + .min_rate = 1300000000u, + .max_rate = 2600000000u, + .c = { + .dbg_name = "dsi0pll_shadow_vco_clk", + .ops = &clk_ops_shadow_dsi_vco, + CLK_INIT(dsi0pll_shadow_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi1pll_vco_clk = { + .ref_clk_rate = 19200000UL, + .min_rate = 1300000000UL, + .max_rate = 2600000000UL, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi1pll_vco_clk_8996", + .ops = &clk_ops_dsi_vco, + CLK_INIT(dsi1pll_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = { + .ref_clk_rate = 19200000u, + .min_rate = 1300000000u, + .max_rate = 2600000000u, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi1pll_shadow_vco_clk", + .ops = &clk_ops_shadow_dsi_vco, + CLK_INIT(dsi1pll_shadow_vco_clk.c), + }, +}; + +static struct div_clk dsi0pll_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &post_n1_div_ops, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_post_n1_div_clk", + .ops = &post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_shadow_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_post_n1_div_ops, + .c = { + .parent = &dsi0pll_shadow_vco_clk.c, + .dbg_name = "dsi0pll_shadow_post_n1_div_clk", + .ops = &shadow_post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &post_n1_div_ops, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_post_n1_div_clk", + .ops = &post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_shadow_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_post_n1_div_ops, + .c = { + .parent = &dsi1pll_shadow_vco_clk.c, + .dbg_name = "dsi1pll_shadow_post_n1_div_clk", + .ops = &shadow_post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &n2_div_ops, + .c = { + .parent = &dsi0pll_post_n1_div_clk.c, + .dbg_name = "dsi0pll_n2_div_clk", + .ops = &n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_n2_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_shadow_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_n2_div_ops, + .c = { + .parent = &dsi0pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi0pll_shadow_n2_div_clk", + .ops = &shadow_n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_n2_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &n2_div_ops, + .c = { + .parent = &dsi1pll_post_n1_div_clk.c, + .dbg_name = "dsi1pll_n2_div_clk", + .ops = &n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_n2_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_shadow_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_n2_div_ops, + .c = { + .parent = &dsi1pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi1pll_shadow_n2_div_clk", + .ops = &shadow_n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_n2_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi0pll_n2_div_clk.c, + .dbg_name = "dsi0pll_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi0pll_shadow_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi0pll_shadow_n2_div_clk.c, + .dbg_name = "dsi0pll_shadow_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi1pll_n2_div_clk.c, + .dbg_name = "dsi1pll_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_shadow_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi1pll_shadow_n2_div_clk.c, + .dbg_name = "dsi1pll_shadow_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_pixel_clk_src.c), + }, +}; + +static struct mux_clk dsi0pll_pixel_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi0pll_pixel_clk_src.c, 0}, + {&dsi0pll_shadow_pixel_clk_src.c, 1}, + }, + .ops = &mdss_pixel_mux_ops, + .c = { + .parent = &dsi0pll_pixel_clk_src.c, + .dbg_name = "dsi0pll_pixel_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pixel_clk_mux.c), + } +}; + +static struct mux_clk dsi1pll_pixel_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi1pll_pixel_clk_src.c, 0}, + {&dsi1pll_shadow_pixel_clk_src.c, 1}, + }, + .ops = &mdss_pixel_mux_ops, + .c = { + .parent = &dsi1pll_pixel_clk_src.c, + .dbg_name = "dsi1pll_pixel_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pixel_clk_mux.c), + } +}; + +static struct div_clk dsi0pll_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi0pll_post_n1_div_clk.c, + .dbg_name = "dsi0pll_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi0pll_byte_clk_src.c), + }, +}; + +static struct div_clk dsi0pll_shadow_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi0pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi0pll_shadow_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi0pll_shadow_byte_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi1pll_post_n1_div_clk.c, + .dbg_name = "dsi1pll_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi1pll_byte_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_shadow_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi1pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi1pll_shadow_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi1pll_shadow_byte_clk_src.c), + }, +}; + +static struct mux_clk dsi0pll_byte_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi0pll_byte_clk_src.c, 0}, + {&dsi0pll_shadow_byte_clk_src.c, 1}, + }, + .ops = &mdss_byte_mux_ops, + .c = { + .parent = &dsi0pll_byte_clk_src.c, + .dbg_name = "dsi0pll_byte_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_byte_clk_mux.c), + } +}; +static struct mux_clk dsi1pll_byte_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi1pll_byte_clk_src.c, 0}, + {&dsi1pll_shadow_byte_clk_src.c, 1}, + }, + .ops = &mdss_byte_mux_ops, + .c = { + .parent = &dsi1pll_byte_clk_src.c, + .dbg_name = "dsi1pll_byte_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_byte_clk_mux.c), + } +}; + +static struct clk_lookup mdss_dsi_pllcc_8996[] = { + CLK_LIST(dsi0pll_byte_clk_mux), + CLK_LIST(dsi0pll_byte_clk_src), + CLK_LIST(dsi0pll_pixel_clk_mux), + CLK_LIST(dsi0pll_pixel_clk_src), + CLK_LIST(dsi0pll_n2_div_clk), + CLK_LIST(dsi0pll_post_n1_div_clk), + CLK_LIST(dsi0pll_vco_clk), + CLK_LIST(dsi0pll_shadow_byte_clk_src), + CLK_LIST(dsi0pll_shadow_pixel_clk_src), + CLK_LIST(dsi0pll_shadow_n2_div_clk), + CLK_LIST(dsi0pll_shadow_post_n1_div_clk), + CLK_LIST(dsi0pll_shadow_vco_clk), +}; + +static struct clk_lookup mdss_dsi_pllcc_8996_1[] = { + CLK_LIST(dsi1pll_byte_clk_mux), + CLK_LIST(dsi1pll_byte_clk_src), + CLK_LIST(dsi1pll_pixel_clk_mux), + CLK_LIST(dsi1pll_pixel_clk_src), + CLK_LIST(dsi1pll_n2_div_clk), + CLK_LIST(dsi1pll_post_n1_div_clk), + CLK_LIST(dsi1pll_vco_clk), + CLK_LIST(dsi1pll_shadow_byte_clk_src), + CLK_LIST(dsi1pll_shadow_pixel_clk_src), + CLK_LIST(dsi1pll_shadow_n2_div_clk), + CLK_LIST(dsi1pll_shadow_post_n1_div_clk), + CLK_LIST(dsi1pll_shadow_vco_clk), +}; + +int dsi_pll_clock_register_8996(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0, ndx; + int const ssc_freq_default = 31500; /* default h/w recommended value */ + int const ssc_ppm_default = 5000; /* default h/w recommended value */ + struct dsi_pll_db *pdb; + + if (!pdev || !pdev->dev.of_node) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + if (!pll_res || !pll_res->pll_base) { + pr_err("Invalid PLL resources\n"); + return -EPROBE_DEFER; + } + + if (pll_res->index >= DSI_PLL_NUM) { + pr_err("pll ndx=%d is NOT supported\n", pll_res->index); + return -EINVAL; + } + + ndx = pll_res->index; + pdb = &pll_db[ndx]; + pll_res->priv = pdb; + pdb->pll = pll_res; + ndx++; + ndx %= DSI_PLL_NUM; + pdb->next = &pll_db[ndx]; + + /* Set clock source operations */ + + /* hr_oclk3, pixel */ + n2_clk_src_ops = clk_ops_slave_div; + n2_clk_src_ops.prepare = mdss_pll_div_prepare; + + shadow_n2_clk_src_ops = clk_ops_slave_div; + + /* hr_ockl2, byte, vco pll */ + post_n1_div_clk_src_ops = clk_ops_div; + post_n1_div_clk_src_ops.prepare = mdss_pll_div_prepare; + + shadow_post_n1_div_clk_src_ops = clk_ops_div; + + byte_clk_src_ops = clk_ops_div; + byte_clk_src_ops.prepare = mdss_pll_div_prepare; + + clk_ops_gen_mux_dsi = clk_ops_gen_mux; + clk_ops_gen_mux_dsi.round_rate = parent_round_rate; + clk_ops_gen_mux_dsi.set_rate = parent_set_rate; + + if (pll_res->ssc_en) { + if (!pll_res->ssc_freq) + pll_res->ssc_freq = ssc_freq_default; + if (!pll_res->ssc_ppm) + pll_res->ssc_ppm = ssc_ppm_default; + } + + /* Set client data to mux, div and vco clocks. */ + if (pll_res->index == DSI_PLL_1) { + dsi1pll_byte_clk_src.priv = pll_res; + dsi1pll_pixel_clk_src.priv = pll_res; + dsi1pll_post_n1_div_clk.priv = pll_res; + dsi1pll_n2_div_clk.priv = pll_res; + dsi1pll_vco_clk.priv = pll_res; + + dsi1pll_shadow_byte_clk_src.priv = pll_res; + dsi1pll_shadow_pixel_clk_src.priv = pll_res; + dsi1pll_shadow_post_n1_div_clk.priv = pll_res; + dsi1pll_shadow_n2_div_clk.priv = pll_res; + dsi1pll_shadow_vco_clk.priv = pll_res; + + pll_res->vco_delay = VCO_DELAY_USEC; + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8996_1, + ARRAY_SIZE(mdss_dsi_pllcc_8996_1)); + } else { + dsi0pll_byte_clk_src.priv = pll_res; + dsi0pll_pixel_clk_src.priv = pll_res; + dsi0pll_post_n1_div_clk.priv = pll_res; + dsi0pll_n2_div_clk.priv = pll_res; + dsi0pll_vco_clk.priv = pll_res; + + dsi0pll_shadow_byte_clk_src.priv = pll_res; + dsi0pll_shadow_pixel_clk_src.priv = pll_res; + dsi0pll_shadow_post_n1_div_clk.priv = pll_res; + dsi0pll_shadow_n2_div_clk.priv = pll_res; + dsi0pll_shadow_vco_clk.priv = pll_res; + + pll_res->vco_delay = VCO_DELAY_USEC; + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8996, + ARRAY_SIZE(mdss_dsi_pllcc_8996)); + } + + if (!rc) { + pr_info("Registered DSI PLL ndx=%d clocks successfully\n", + pll_res->index); + } + + return rc; +} diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h new file mode 100644 index 000000000000..611e79101d4f --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MDSS_DSI_PLL_8996_H +#define MDSS_DSI_PLL_8996_H + +#define DSIPHY_CMN_CLK_CFG0 0x0010 +#define DSIPHY_CMN_CLK_CFG1 0x0014 +#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 + +#define DSIPHY_CMN_PLL_CNTRL 0x0048 +#define DSIPHY_CMN_CTRL_0 0x001c +#define DSIPHY_CMN_CTRL_1 0x0020 + +#define DSIPHY_CMN_LDO_CNTRL 0x004c + +#define DSIPHY_PLL_IE_TRIM 0x0400 +#define DSIPHY_PLL_IP_TRIM 0x0404 + +#define DSIPHY_PLL_IPTAT_TRIM 0x0410 + +#define DSIPHY_PLL_CLKBUFLR_EN 0x041c + +#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428 +#define DSIPHY_PLL_RESETSM_CNTRL 0x042c +#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430 +#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434 +#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438 +#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c +#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440 +#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444 +#define DSIPHY_PLL_KVCO_COUNT1 0x0448 +#define DSIPHY_PLL_KVCO_COUNT2 0x044c +#define DSIPHY_PLL_VREF_CFG1 0x045c + +#define DSIPHY_PLL_KVCO_CODE 0x0458 + +#define DSIPHY_PLL_VCO_DIV_REF1 0x046c +#define DSIPHY_PLL_VCO_DIV_REF2 0x0470 +#define DSIPHY_PLL_VCO_COUNT1 0x0474 +#define DSIPHY_PLL_VCO_COUNT2 0x0478 +#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c +#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480 +#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484 +#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488 +#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C +#define DSIPHY_PLL_DEC_START 0x0490 +#define DSIPHY_PLL_SSC_EN_CENTER 0x0494 +#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498 +#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c +#define DSIPHY_PLL_SSC_PER1 0x04a0 +#define DSIPHY_PLL_SSC_PER2 0x04a4 +#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8 +#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac +#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4 +#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8 +#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc +#define DSIPHY_PLL_TXCLK_EN 0x04c0 +#define DSIPHY_PLL_PLL_CRCTRL 0x04c4 + +#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc + +#define DSIPHY_PLL_PLL_MISC1 0x04e8 + +#define DSIPHY_PLL_CP_SET_CUR 0x04f0 +#define DSIPHY_PLL_PLL_ICPMSET 0x04f4 +#define DSIPHY_PLL_PLL_ICPCSET 0x04f8 +#define DSIPHY_PLL_PLL_ICP_SET 0x04fc +#define DSIPHY_PLL_PLL_LPF1 0x0500 +#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504 +#define DSIPHY_PLL_PLL_BANDGAP 0x0508 + +#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C +#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C +#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088 +#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094 +#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098 + +struct dsi_pll_input { + u32 fref; /* 19.2 Mhz, reference clk */ + u32 fdata; /* bit clock rate */ + u32 dsiclk_sel; /* 1, reg: 0x0014 */ + u32 n2div; /* 1, reg: 0x0010, bit 4-7 */ + u32 ssc_en; /* 1, reg: 0x0494, bit 0 */ + u32 ldo_en; /* 0, reg: 0x004c, bit 0 */ + + /* fixed */ + u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */ + u32 vco_measure_time; /* 5, unknown */ + u32 kvco_measure_time; /* 5, unknown */ + u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */ + u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */ + u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */ + u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */ + u32 ssc_center; /* 0, reg: 0x0494, bit 1 */ + u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */ + u32 ssc_spread; /* 0.005 */ + u32 ssc_freq; /* unknown */ + u32 pll_ie_trim; /* 4, reg: 0x0400 */ + u32 pll_ip_trim; /* 4, reg: 0x0404 */ + u32 pll_iptat_trim; /* reg: 0x0410 */ + u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */ + u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */ + + u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */ + u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */ + + u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */ + u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */ + + u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */ + u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */ + + u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */ + u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */ + u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */ + u32 pll_c3ctrl; /* 2, reg: 0x04c4 */ + u32 pll_r3ctrl; /* 1, reg: 0x04c4 */ +}; + +struct dsi_pll_output { + u32 pll_txclk_en; /* reg: 0x04c0 */ + u32 dec_start; /* reg: 0x0490 */ + u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */ + u32 ssc_period; /* reg: 0x04a0, 0x04a4 */ + u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */ + u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */ + u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */ + u32 pll_vco_count; /* reg: 0x0474, 0x0478 */ + u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */ + u32 pll_kvco_count; /* reg: 0x0448, 0x044c */ + u32 pll_misc1; /* reg: 0x04e8 */ + u32 pll_lpf2_postdiv; /* reg: 0x0504 */ + u32 pll_resetsm_cntrl; /* reg: 0x042c */ + u32 pll_resetsm_cntrl2; /* reg: 0x0430 */ + u32 pll_resetsm_cntrl5; /* reg: 0x043c */ + u32 pll_kvco_code; /* reg: 0x0458 */ + + u32 cmn_clk_cfg0; /* reg: 0x0010 */ + u32 cmn_clk_cfg1; /* reg: 0x0014 */ + u32 cmn_ldo_cntrl; /* reg: 0x004c */ + + u32 pll_postdiv; /* vco */ + u32 pll_n1div; /* vco */ + u32 pll_n2div; /* hr_oclk3, pixel */ + u32 fcvo; +}; + +enum { + DSI_PLL_0, + DSI_PLL_1, + DSI_PLL_NUM +}; + +struct dsi_pll_db { + struct dsi_pll_db *next; + struct mdss_pll_resources *pll; + struct dsi_pll_input in; + struct dsi_pll_output out; + int source_setup_done; +}; + +enum { + PLL_OUTPUT_NONE, + PLL_OUTPUT_RIGHT, + PLL_OUTPUT_LEFT, + PLL_OUTPUT_BOTH +}; + +enum { + PLL_SOURCE_FROM_LEFT, + PLL_SOURCE_FROM_RIGHT +}; + +enum { + PLL_UNKNOWN, + PLL_STANDALONE, + PLL_SLAVE, + PLL_MASTER +}; + +int pll_vco_set_rate_8996(struct clk *c, unsigned long rate); +long pll_vco_round_rate_8996(struct clk *c, unsigned long rate); +enum handoff pll_vco_handoff_8996(struct clk *c); +enum handoff shadow_pll_vco_handoff_8996(struct clk *c); +int shadow_post_n1_div_set_div(struct div_clk *clk, int div); +int shadow_post_n1_div_get_div(struct div_clk *clk); +int shadow_n2_div_set_div(struct div_clk *clk, int div); +int shadow_n2_div_get_div(struct div_clk *clk); +int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate); +int pll_vco_prepare_8996(struct clk *c); +void pll_vco_unprepare_8996(struct clk *c); +int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel); +int get_mdss_byte_mux_sel_8996(struct mux_clk *clk); +int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel); +int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk); +int post_n1_div_set_div(struct div_clk *clk, int div); +int post_n1_div_get_div(struct div_clk *clk); +int n2_div_set_div(struct div_clk *clk, int div); +int n2_div_get_div(struct div_clk *clk); +int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll); + +#endif /* MDSS_DSI_PLL_8996_H */ diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll.h b/drivers/clk/qcom/mdss/mdss-dsi-pll.h new file mode 100644 index 000000000000..f88ae4d0eea1 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_DSI_PLL_H +#define __MDSS_DSI_PLL_H + +#define MAX_DSI_PLL_EN_SEQS 10 + +#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) +#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070) + +/* Register offsets for 20nm PHY PLL */ +#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014) +#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C) +#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C) + +struct lpfr_cfg { + unsigned long vco_rate; + u32 r; +}; + +struct dsi_pll_vco_clk { + unsigned long ref_clk_rate; + unsigned long min_rate; + unsigned long max_rate; + u32 pll_en_seq_cnt; + struct lpfr_cfg *lpfr_lut; + u32 lpfr_lut_size; + void *priv; + + struct clk c; + + int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS]) + (struct mdss_pll_resources *dsi_pll_Res); +}; + +static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) +{ + return container_of(clk, struct dsi_pll_vco_clk, c); +} + +int dsi_pll_clock_register_hpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_20nm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_lpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_8996(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_cobalt(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int set_byte_mux_sel(struct mux_clk *clk, int sel); +int get_byte_mux_sel(struct mux_clk *clk); +int dsi_pll_mux_prepare(struct clk *c); +int fixed_4div_set_div(struct div_clk *clk, int div); +int fixed_4div_get_div(struct div_clk *clk); +int digital_set_div(struct div_clk *clk, int div); +int digital_get_div(struct div_clk *clk); +int analog_set_div(struct div_clk *clk, int div); +int analog_get_div(struct div_clk *clk); +int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res); +int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate); +unsigned long vco_get_rate(struct clk *c); +long vco_round_rate(struct clk *c, unsigned long rate); +enum handoff vco_handoff(struct clk *c); +int vco_prepare(struct clk *c); +void vco_unprepare(struct clk *c); + +/* APIs for 20nm PHY PLL */ +int pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate); +int shadow_pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, + unsigned long rate); +long pll_20nm_vco_round_rate(struct clk *c, unsigned long rate); +enum handoff pll_20nm_vco_handoff(struct clk *c); +int pll_20nm_vco_prepare(struct clk *c); +void pll_20nm_vco_unprepare(struct clk *c); +int pll_20nm_vco_enable_seq(struct mdss_pll_resources *dsi_pll_res); + +int set_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel); +int set_shadow_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel); +int get_bypass_lp_div_mux_sel(struct mux_clk *clk); +int fixed_hr_oclk2_set_div(struct div_clk *clk, int div); +int shadow_fixed_hr_oclk2_set_div(struct div_clk *clk, int div); +int fixed_hr_oclk2_get_div(struct div_clk *clk); +int hr_oclk3_set_div(struct div_clk *clk, int div); +int shadow_hr_oclk3_set_div(struct div_clk *clk, int div); +int hr_oclk3_get_div(struct div_clk *clk); +int ndiv_set_div(struct div_clk *clk, int div); +int shadow_ndiv_set_div(struct div_clk *clk, int div); +int ndiv_get_div(struct div_clk *clk); +void __dsi_pll_disable(void __iomem *pll_base); + +int set_mdss_pixel_mux_sel(struct mux_clk *clk, int sel); +int get_mdss_pixel_mux_sel(struct mux_clk *clk); +int set_mdss_byte_mux_sel(struct mux_clk *clk, int sel); +int get_mdss_byte_mux_sel(struct mux_clk *clk); + +#endif diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c new file mode 100644 index 000000000000..e79b5cc39a79 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c @@ -0,0 +1,2686 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> +#include <dt-bindings/clock/msm-clocks-8996.h> + +#include "mdss-pll.h" +#include "mdss-hdmi-pll.h" + +/* CONSTANTS */ +#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10 +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000 +#define HDMI_CLKS_PLL_DIVSEL 0 +#define HDMI_CORECLK_DIV 5 +#define HDMI_REF_CLOCK 19200000 +#define HDMI_64B_ERR_VAL 0xFFFFFFFFFFFFFFFF +#define HDMI_VERSION_8996_V1 1 +#define HDMI_VERSION_8996_V2 2 +#define HDMI_VERSION_8996_V3 3 +#define HDMI_VERSION_8996_V3_1_8 4 + +#define HDMI_VCO_MAX_FREQ 12000000000 +#define HDMI_VCO_MIN_FREQ 8000000000 +#define HDMI_2400MHZ_BIT_CLK_HZ 2400000000UL +#define HDMI_2250MHZ_BIT_CLK_HZ 2250000000UL +#define HDMI_2000MHZ_BIT_CLK_HZ 2000000000UL +#define HDMI_1700MHZ_BIT_CLK_HZ 1700000000UL +#define HDMI_1200MHZ_BIT_CLK_HZ 1200000000UL +#define HDMI_1334MHZ_BIT_CLK_HZ 1334000000UL +#define HDMI_1000MHZ_BIT_CLK_HZ 1000000000UL +#define HDMI_850MHZ_BIT_CLK_HZ 850000000 +#define HDMI_667MHZ_BIT_CLK_HZ 667000000 +#define HDMI_600MHZ_BIT_CLK_HZ 600000000 +#define HDMI_500MHZ_BIT_CLK_HZ 500000000 +#define HDMI_450MHZ_BIT_CLK_HZ 450000000 +#define HDMI_334MHZ_BIT_CLK_HZ 334000000 +#define HDMI_300MHZ_BIT_CLK_HZ 300000000 +#define HDMI_282MHZ_BIT_CLK_HZ 282000000 +#define HDMI_250MHZ_BIT_CLK_HZ 250000000 +#define HDMI_KHZ_TO_HZ 1000 + +/* PLL REGISTERS */ +#define QSERDES_COM_ATB_SEL1 (0x000) +#define QSERDES_COM_ATB_SEL2 (0x004) +#define QSERDES_COM_FREQ_UPDATE (0x008) +#define QSERDES_COM_BG_TIMER (0x00C) +#define QSERDES_COM_SSC_EN_CENTER (0x010) +#define QSERDES_COM_SSC_ADJ_PER1 (0x014) +#define QSERDES_COM_SSC_ADJ_PER2 (0x018) +#define QSERDES_COM_SSC_PER1 (0x01C) +#define QSERDES_COM_SSC_PER2 (0x020) +#define QSERDES_COM_SSC_STEP_SIZE1 (0x024) +#define QSERDES_COM_SSC_STEP_SIZE2 (0x028) +#define QSERDES_COM_POST_DIV (0x02C) +#define QSERDES_COM_POST_DIV_MUX (0x030) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x034) +#define QSERDES_COM_CLK_ENABLE1 (0x038) +#define QSERDES_COM_SYS_CLK_CTRL (0x03C) +#define QSERDES_COM_SYSCLK_BUF_ENABLE (0x040) +#define QSERDES_COM_PLL_EN (0x044) +#define QSERDES_COM_PLL_IVCO (0x048) +#define QSERDES_COM_LOCK_CMP1_MODE0 (0x04C) +#define QSERDES_COM_LOCK_CMP2_MODE0 (0x050) +#define QSERDES_COM_LOCK_CMP3_MODE0 (0x054) +#define QSERDES_COM_LOCK_CMP1_MODE1 (0x058) +#define QSERDES_COM_LOCK_CMP2_MODE1 (0x05C) +#define QSERDES_COM_LOCK_CMP3_MODE1 (0x060) +#define QSERDES_COM_LOCK_CMP1_MODE2 (0x064) +#define QSERDES_COM_CMN_RSVD0 (0x064) +#define QSERDES_COM_LOCK_CMP2_MODE2 (0x068) +#define QSERDES_COM_EP_CLOCK_DETECT_CTRL (0x068) +#define QSERDES_COM_LOCK_CMP3_MODE2 (0x06C) +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS (0x06C) +#define QSERDES_COM_BG_TRIM (0x070) +#define QSERDES_COM_CLK_EP_DIV (0x074) +#define QSERDES_COM_CP_CTRL_MODE0 (0x078) +#define QSERDES_COM_CP_CTRL_MODE1 (0x07C) +#define QSERDES_COM_CP_CTRL_MODE2 (0x080) +#define QSERDES_COM_CMN_RSVD1 (0x080) +#define QSERDES_COM_PLL_RCTRL_MODE0 (0x084) +#define QSERDES_COM_PLL_RCTRL_MODE1 (0x088) +#define QSERDES_COM_PLL_RCTRL_MODE2 (0x08C) +#define QSERDES_COM_CMN_RSVD2 (0x08C) +#define QSERDES_COM_PLL_CCTRL_MODE0 (0x090) +#define QSERDES_COM_PLL_CCTRL_MODE1 (0x094) +#define QSERDES_COM_PLL_CCTRL_MODE2 (0x098) +#define QSERDES_COM_CMN_RSVD3 (0x098) +#define QSERDES_COM_PLL_CNTRL (0x09C) +#define QSERDES_COM_PHASE_SEL_CTRL (0x0A0) +#define QSERDES_COM_PHASE_SEL_DC (0x0A4) +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL (0x0A8) +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM (0x0A8) +#define QSERDES_COM_SYSCLK_EN_SEL (0x0AC) +#define QSERDES_COM_CML_SYSCLK_SEL (0x0B0) +#define QSERDES_COM_RESETSM_CNTRL (0x0B4) +#define QSERDES_COM_RESETSM_CNTRL2 (0x0B8) +#define QSERDES_COM_RESTRIM_CTRL (0x0BC) +#define QSERDES_COM_RESTRIM_CTRL2 (0x0C0) +#define QSERDES_COM_RESCODE_DIV_NUM (0x0C4) +#define QSERDES_COM_LOCK_CMP_EN (0x0C8) +#define QSERDES_COM_LOCK_CMP_CFG (0x0CC) +#define QSERDES_COM_DEC_START_MODE0 (0x0D0) +#define QSERDES_COM_DEC_START_MODE1 (0x0D4) +#define QSERDES_COM_DEC_START_MODE2 (0x0D8) +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL (0x0D8) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 (0x0DC) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 (0x0E0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 (0x0E4) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 (0x0E8) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 (0x0EC) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 (0x0F0) +#define QSERDES_COM_DIV_FRAC_START1_MODE2 (0x0F4) +#define QSERDES_COM_VCO_TUNE_MINVAL1 (0x0F4) +#define QSERDES_COM_DIV_FRAC_START2_MODE2 (0x0F8) +#define QSERDES_COM_VCO_TUNE_MINVAL2 (0x0F8) +#define QSERDES_COM_DIV_FRAC_START3_MODE2 (0x0FC) +#define QSERDES_COM_CMN_RSVD4 (0x0FC) +#define QSERDES_COM_INTEGLOOP_INITVAL (0x100) +#define QSERDES_COM_INTEGLOOP_EN (0x104) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 (0x108) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 (0x10C) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 (0x110) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 (0x114) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 (0x118) +#define QSERDES_COM_VCO_TUNE_MAXVAL1 (0x118) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 (0x11C) +#define QSERDES_COM_VCO_TUNE_MAXVAL2 (0x11C) +#define QSERDES_COM_RES_TRIM_CONTROL2 (0x120) +#define QSERDES_COM_VCO_TUNE_CTRL (0x124) +#define QSERDES_COM_VCO_TUNE_MAP (0x128) +#define QSERDES_COM_VCO_TUNE1_MODE0 (0x12C) +#define QSERDES_COM_VCO_TUNE2_MODE0 (0x130) +#define QSERDES_COM_VCO_TUNE1_MODE1 (0x134) +#define QSERDES_COM_VCO_TUNE2_MODE1 (0x138) +#define QSERDES_COM_VCO_TUNE1_MODE2 (0x13C) +#define QSERDES_COM_VCO_TUNE_INITVAL1 (0x13C) +#define QSERDES_COM_VCO_TUNE2_MODE2 (0x140) +#define QSERDES_COM_VCO_TUNE_INITVAL2 (0x140) +#define QSERDES_COM_VCO_TUNE_TIMER1 (0x144) +#define QSERDES_COM_VCO_TUNE_TIMER2 (0x148) +#define QSERDES_COM_SAR (0x14C) +#define QSERDES_COM_SAR_CLK (0x150) +#define QSERDES_COM_SAR_CODE_OUT_STATUS (0x154) +#define QSERDES_COM_SAR_CODE_READY_STATUS (0x158) +#define QSERDES_COM_CMN_STATUS (0x15C) +#define QSERDES_COM_RESET_SM_STATUS (0x160) +#define QSERDES_COM_RESTRIM_CODE_STATUS (0x164) +#define QSERDES_COM_PLLCAL_CODE1_STATUS (0x168) +#define QSERDES_COM_PLLCAL_CODE2_STATUS (0x16C) +#define QSERDES_COM_BG_CTRL (0x170) +#define QSERDES_COM_CLK_SELECT (0x174) +#define QSERDES_COM_HSCLK_SEL (0x178) +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS (0x17C) +#define QSERDES_COM_PLL_ANALOG (0x180) +#define QSERDES_COM_CORECLK_DIV (0x184) +#define QSERDES_COM_SW_RESET (0x188) +#define QSERDES_COM_CORE_CLK_EN (0x18C) +#define QSERDES_COM_C_READY_STATUS (0x190) +#define QSERDES_COM_CMN_CONFIG (0x194) +#define QSERDES_COM_CMN_RATE_OVERRIDE (0x198) +#define QSERDES_COM_SVS_MODE_CLK_SEL (0x19C) +#define QSERDES_COM_DEBUG_BUS0 (0x1A0) +#define QSERDES_COM_DEBUG_BUS1 (0x1A4) +#define QSERDES_COM_DEBUG_BUS2 (0x1A8) +#define QSERDES_COM_DEBUG_BUS3 (0x1AC) +#define QSERDES_COM_DEBUG_BUS_SEL (0x1B0) +#define QSERDES_COM_CMN_MISC1 (0x1B4) +#define QSERDES_COM_CMN_MISC2 (0x1B8) +#define QSERDES_COM_CORECLK_DIV_MODE1 (0x1BC) +#define QSERDES_COM_CORECLK_DIV_MODE2 (0x1C0) +#define QSERDES_COM_CMN_RSVD5 (0x1C0) + +/* Tx Channel base addresses */ +#define HDMI_TX_L0_BASE_OFFSET (0x400) +#define HDMI_TX_L1_BASE_OFFSET (0x600) +#define HDMI_TX_L2_BASE_OFFSET (0x800) +#define HDMI_TX_L3_BASE_OFFSET (0xA00) + +/* Tx Channel PHY registers */ +#define QSERDES_TX_L0_BIST_MODE_LANENO (0x000) +#define QSERDES_TX_L0_BIST_INVERT (0x004) +#define QSERDES_TX_L0_CLKBUF_ENABLE (0x008) +#define QSERDES_TX_L0_CMN_CONTROL_ONE (0x00C) +#define QSERDES_TX_L0_CMN_CONTROL_TWO (0x010) +#define QSERDES_TX_L0_CMN_CONTROL_THREE (0x014) +#define QSERDES_TX_L0_TX_EMP_POST1_LVL (0x018) +#define QSERDES_TX_L0_TX_POST2_EMPH (0x01C) +#define QSERDES_TX_L0_TX_BOOST_LVL_UP_DN (0x020) +#define QSERDES_TX_L0_HP_PD_ENABLES (0x024) +#define QSERDES_TX_L0_TX_IDLE_LVL_LARGE_AMP (0x028) +#define QSERDES_TX_L0_TX_DRV_LVL (0x02C) +#define QSERDES_TX_L0_TX_DRV_LVL_OFFSET (0x030) +#define QSERDES_TX_L0_RESET_TSYNC_EN (0x034) +#define QSERDES_TX_L0_PRE_STALL_LDO_BOOST_EN (0x038) +#define QSERDES_TX_L0_TX_BAND (0x03C) +#define QSERDES_TX_L0_SLEW_CNTL (0x040) +#define QSERDES_TX_L0_INTERFACE_SELECT (0x044) +#define QSERDES_TX_L0_LPB_EN (0x048) +#define QSERDES_TX_L0_RES_CODE_LANE_TX (0x04C) +#define QSERDES_TX_L0_RES_CODE_LANE_RX (0x050) +#define QSERDES_TX_L0_RES_CODE_LANE_OFFSET (0x054) +#define QSERDES_TX_L0_PERL_LENGTH1 (0x058) +#define QSERDES_TX_L0_PERL_LENGTH2 (0x05C) +#define QSERDES_TX_L0_SERDES_BYP_EN_OUT (0x060) +#define QSERDES_TX_L0_DEBUG_BUS_SEL (0x064) +#define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x068) +#define QSERDES_TX_L0_TX_POL_INV (0x06C) +#define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN (0x070) +#define QSERDES_TX_L0_BIST_PATTERN1 (0x074) +#define QSERDES_TX_L0_BIST_PATTERN2 (0x078) +#define QSERDES_TX_L0_BIST_PATTERN3 (0x07C) +#define QSERDES_TX_L0_BIST_PATTERN4 (0x080) +#define QSERDES_TX_L0_BIST_PATTERN5 (0x084) +#define QSERDES_TX_L0_BIST_PATTERN6 (0x088) +#define QSERDES_TX_L0_BIST_PATTERN7 (0x08C) +#define QSERDES_TX_L0_BIST_PATTERN8 (0x090) +#define QSERDES_TX_L0_LANE_MODE (0x094) +#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE (0x098) +#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION (0x09C) +#define QSERDES_TX_L0_ATB_SEL1 (0x0A0) +#define QSERDES_TX_L0_ATB_SEL2 (0x0A4) +#define QSERDES_TX_L0_RCV_DETECT_LVL (0x0A8) +#define QSERDES_TX_L0_RCV_DETECT_LVL_2 (0x0AC) +#define QSERDES_TX_L0_PRBS_SEED1 (0x0B0) +#define QSERDES_TX_L0_PRBS_SEED2 (0x0B4) +#define QSERDES_TX_L0_PRBS_SEED3 (0x0B8) +#define QSERDES_TX_L0_PRBS_SEED4 (0x0BC) +#define QSERDES_TX_L0_RESET_GEN (0x0C0) +#define QSERDES_TX_L0_RESET_GEN_MUXES (0x0C4) +#define QSERDES_TX_L0_TRAN_DRVR_EMP_EN (0x0C8) +#define QSERDES_TX_L0_TX_INTERFACE_MODE (0x0CC) +#define QSERDES_TX_L0_PWM_CTRL (0x0D0) +#define QSERDES_TX_L0_PWM_ENCODED_OR_DATA (0x0D4) +#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND2 (0x0D8) +#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND2 (0x0DC) +#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND2 (0x0E0) +#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND2 (0x0E4) +#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND0_1 (0x0E8) +#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND0_1 (0x0EC) +#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND0_1 (0x0F0) +#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND0_1 (0x0F4) +#define QSERDES_TX_L0_VMODE_CTRL1 (0x0F8) +#define QSERDES_TX_L0_VMODE_CTRL2 (0x0FC) +#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL (0x100) +#define QSERDES_TX_L0_BIST_STATUS (0x104) +#define QSERDES_TX_L0_BIST_ERROR_COUNT1 (0x108) +#define QSERDES_TX_L0_BIST_ERROR_COUNT2 (0x10C) +#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV (0x110) + +/* HDMI PHY REGISTERS */ +#define HDMI_PHY_BASE_OFFSET (0xC00) + +#define HDMI_PHY_CFG (0x00) +#define HDMI_PHY_PD_CTL (0x04) +#define HDMI_PHY_MODE (0x08) +#define HDMI_PHY_MISR_CLEAR (0x0C) +#define HDMI_PHY_TX0_TX1_BIST_CFG0 (0x10) +#define HDMI_PHY_TX0_TX1_BIST_CFG1 (0x14) +#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0 (0x18) +#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1 (0x1C) +#define HDMI_PHY_TX0_TX1_BIST_PATTERN0 (0x20) +#define HDMI_PHY_TX0_TX1_BIST_PATTERN1 (0x24) +#define HDMI_PHY_TX2_TX3_BIST_CFG0 (0x28) +#define HDMI_PHY_TX2_TX3_BIST_CFG1 (0x2C) +#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0 (0x30) +#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1 (0x34) +#define HDMI_PHY_TX2_TX3_BIST_PATTERN0 (0x38) +#define HDMI_PHY_TX2_TX3_BIST_PATTERN1 (0x3C) +#define HDMI_PHY_DEBUG_BUS_SEL (0x40) +#define HDMI_PHY_TXCAL_CFG0 (0x44) +#define HDMI_PHY_TXCAL_CFG1 (0x48) +#define HDMI_PHY_TX0_TX1_LANE_CTL (0x4C) +#define HDMI_PHY_TX2_TX3_LANE_CTL (0x50) +#define HDMI_PHY_LANE_BIST_CONFIG (0x54) +#define HDMI_PHY_CLOCK (0x58) +#define HDMI_PHY_MISC1 (0x5C) +#define HDMI_PHY_MISC2 (0x60) +#define HDMI_PHY_TX0_TX1_BIST_STATUS0 (0x64) +#define HDMI_PHY_TX0_TX1_BIST_STATUS1 (0x68) +#define HDMI_PHY_TX0_TX1_BIST_STATUS2 (0x6C) +#define HDMI_PHY_TX2_TX3_BIST_STATUS0 (0x70) +#define HDMI_PHY_TX2_TX3_BIST_STATUS1 (0x74) +#define HDMI_PHY_TX2_TX3_BIST_STATUS2 (0x78) +#define HDMI_PHY_PRE_MISR_STATUS0 (0x7C) +#define HDMI_PHY_PRE_MISR_STATUS1 (0x80) +#define HDMI_PHY_PRE_MISR_STATUS2 (0x84) +#define HDMI_PHY_PRE_MISR_STATUS3 (0x88) +#define HDMI_PHY_POST_MISR_STATUS0 (0x8C) +#define HDMI_PHY_POST_MISR_STATUS1 (0x90) +#define HDMI_PHY_POST_MISR_STATUS2 (0x94) +#define HDMI_PHY_POST_MISR_STATUS3 (0x98) +#define HDMI_PHY_STATUS (0x9C) +#define HDMI_PHY_MISC3_STATUS (0xA0) +#define HDMI_PHY_MISC4_STATUS (0xA4) +#define HDMI_PHY_DEBUG_BUS0 (0xA8) +#define HDMI_PHY_DEBUG_BUS1 (0xAC) +#define HDMI_PHY_DEBUG_BUS2 (0xB0) +#define HDMI_PHY_DEBUG_BUS3 (0xB4) +#define HDMI_PHY_PHY_REVISION_ID0 (0xB8) +#define HDMI_PHY_PHY_REVISION_ID1 (0xBC) +#define HDMI_PHY_PHY_REVISION_ID2 (0xC0) +#define HDMI_PHY_PHY_REVISION_ID3 (0xC4) + +#define HDMI_PLL_POLL_MAX_READS 100 +#define HDMI_PLL_POLL_TIMEOUT_US 1500 + +enum hdmi_pll_freqs { + HDMI_PCLK_25200_KHZ, + HDMI_PCLK_27027_KHZ, + HDMI_PCLK_27000_KHZ, + HDMI_PCLK_74250_KHZ, + HDMI_PCLK_148500_KHZ, + HDMI_PCLK_154000_KHZ, + HDMI_PCLK_268500_KHZ, + HDMI_PCLK_297000_KHZ, + HDMI_PCLK_594000_KHZ, + HDMI_PCLK_MAX +}; + +struct hdmi_8996_phy_pll_reg_cfg { + u32 tx_l0_lane_mode; + u32 tx_l2_lane_mode; + u32 tx_l0_tx_band; + u32 tx_l1_tx_band; + u32 tx_l2_tx_band; + u32 tx_l3_tx_band; + u32 com_svs_mode_clk_sel; + u32 com_hsclk_sel; + u32 com_pll_cctrl_mode0; + u32 com_pll_rctrl_mode0; + u32 com_cp_ctrl_mode0; + u32 com_dec_start_mode0; + u32 com_div_frac_start1_mode0; + u32 com_div_frac_start2_mode0; + u32 com_div_frac_start3_mode0; + u32 com_integloop_gain0_mode0; + u32 com_integloop_gain1_mode0; + u32 com_lock_cmp_en; + u32 com_lock_cmp1_mode0; + u32 com_lock_cmp2_mode0; + u32 com_lock_cmp3_mode0; + u32 com_core_clk_en; + u32 com_coreclk_div; + u32 com_restrim_ctrl; + u32 com_vco_tune_ctrl; + + u32 tx_l0_tx_drv_lvl; + u32 tx_l0_tx_emp_post1_lvl; + u32 tx_l1_tx_drv_lvl; + u32 tx_l1_tx_emp_post1_lvl; + u32 tx_l2_tx_drv_lvl; + u32 tx_l2_tx_emp_post1_lvl; + u32 tx_l3_tx_drv_lvl; + u32 tx_l3_tx_emp_post1_lvl; + u32 tx_l0_vmode_ctrl1; + u32 tx_l0_vmode_ctrl2; + u32 tx_l1_vmode_ctrl1; + u32 tx_l1_vmode_ctrl2; + u32 tx_l2_vmode_ctrl1; + u32 tx_l2_vmode_ctrl2; + u32 tx_l3_vmode_ctrl1; + u32 tx_l3_vmode_ctrl2; + u32 tx_l0_res_code_lane_tx; + u32 tx_l1_res_code_lane_tx; + u32 tx_l2_res_code_lane_tx; + u32 tx_l3_res_code_lane_tx; + + u32 phy_mode; +}; + +struct hdmi_8996_v3_post_divider { + u64 vco_freq; + u64 hsclk_divsel; + u64 vco_ratio; + u64 tx_band_sel; + u64 half_rate_mode; +}; + +static inline struct hdmi_pll_vco_clk *to_hdmi_8996_vco_clk(struct clk *clk) +{ + return container_of(clk, struct hdmi_pll_vco_clk, c); +} + +static inline u64 hdmi_8996_v1_get_post_div_lt_2g(u64 bclk) +{ + if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_1700MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_1200MHZ_BIT_CLK_HZ) + return 4; + else if (bclk >= HDMI_850MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_600MHZ_BIT_CLK_HZ) + return 4; + else if (bclk >= HDMI_450MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_300MHZ_BIT_CLK_HZ) + return 4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_post_div_lt_2g(u64 bclk, u64 vco_range) +{ + u64 hdmi_8ghz = vco_range; + u64 tmp_calc; + + hdmi_8ghz <<= 2; + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 6U); + + if (bclk >= vco_range) + return 2; + else if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 1) + return 4; + + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 12U); + if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 2) + return 4; + + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 24U); + if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 3) + return 4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_post_div_gt_2g(u64 hsclk) +{ + if (hsclk >= 0 && hsclk <= 3) + return hsclk + 1; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_get_coreclk_div_lt_2g(u64 bclk) +{ + if (bclk >= HDMI_1334MHZ_BIT_CLK_HZ) + return 1; + else if (bclk >= HDMI_1000MHZ_BIT_CLK_HZ) + return 1; + else if (bclk >= HDMI_667MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_500MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_334MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_250MHZ_BIT_CLK_HZ) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_get_coreclk_div_ratio(u64 clks_pll_divsel, + u64 coreclk_div) +{ + if (clks_pll_divsel == 0) + return coreclk_div*2; + else if (clks_pll_divsel == 1) + return coreclk_div*4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v1_get_tx_band(u64 bclk) +{ + if (bclk >= 2400000000UL) + return 0; + if (bclk >= 1200000000UL) + return 1; + if (bclk >= 600000000UL) + return 2; + if (bclk >= 300000000UL) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_tx_band(u64 bclk, u64 vco_range) +{ + if (bclk >= vco_range) + return 0; + else if (bclk >= vco_range >> 1) + return 1; + else if (bclk >= vco_range >> 2) + return 2; + else if (bclk >= vco_range >> 3) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v1_get_hsclk(u64 fdata) +{ + if (fdata >= 9600000000UL) + return 0; + else if (fdata >= 4800000000UL) + return 1; + else if (fdata >= 3200000000UL) + return 2; + else if (fdata >= 2400000000UL) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_hsclk(u64 fdata, u64 vco_range) +{ + u64 tmp_calc = vco_range; + + tmp_calc <<= 2; + do_div(tmp_calc, 3U); + if (fdata >= (vco_range << 2)) + return 0; + else if (fdata >= (vco_range << 1)) + return 1; + else if (fdata >= tmp_calc) + return 2; + else if (fdata >= vco_range) + return 3; + + return HDMI_64B_ERR_VAL; + +} + +static inline u64 hdmi_8996_v2_get_vco_freq(u64 bclk, u64 vco_range) +{ + u64 tx_band_div_ratio = 1U << hdmi_8996_v2_get_tx_band(bclk, vco_range); + u64 pll_post_div_ratio; + + if (bclk >= vco_range) { + u64 hsclk = hdmi_8996_v2_get_hsclk(bclk, vco_range); + + pll_post_div_ratio = hdmi_8996_v2_get_post_div_gt_2g(hsclk); + } else { + pll_post_div_ratio = hdmi_8996_v2_get_post_div_lt_2g(bclk, + vco_range); + } + + return bclk * (pll_post_div_ratio * tx_band_div_ratio); +} + +static inline u64 hdmi_8996_v2_get_fdata(u64 bclk, u64 vco_range) +{ + if (bclk >= vco_range) + return bclk; + + u64 tmp_calc = hdmi_8996_v2_get_vco_freq(bclk, vco_range); + u64 pll_post_div_ratio_lt_2g = hdmi_8996_v2_get_post_div_lt_2g( + bclk, vco_range); + if (pll_post_div_ratio_lt_2g == HDMI_64B_ERR_VAL) + return HDMI_64B_ERR_VAL; + + do_div(tmp_calc, pll_post_div_ratio_lt_2g); + return tmp_calc; +} + +static inline u64 hdmi_8996_get_cpctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || + (gen_ssc == true)) + /* + * This should be ROUND(11/(19.2/20))). + * Since ref clock does not change, hardcoding to 11 + */ + return 0xB; + + return 0x23; +} + +static inline u64 hdmi_8996_get_rctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x16; + + return 0x10; +} + +static inline u64 hdmi_8996_get_cctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x28; + + return 0x1; +} + +static inline u64 hdmi_8996_get_integloop_gain(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x80; + + return 0xC4; +} + +static inline u64 hdmi_8996_v3_get_integloop_gain(u64 frac_start, u64 bclk, + bool gen_ssc) +{ + u64 digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; + u64 base = ((frac_start != 0) || (gen_ssc == true)) ? 0x40 : 0xC4; + + base <<= digclk_divsel; + + return (base <= 2046 ? base : 0x7FE); +} + +static inline u64 hdmi_8996_get_vco_tune(u64 fdata, u64 div) +{ + u64 vco_tune; + + vco_tune = fdata * div; + do_div(vco_tune, 1000000); + vco_tune = 13000 - vco_tune - 256; + do_div(vco_tune, 5); + + return vco_tune; +} + +static inline u64 hdmi_8996_get_pll_cmp(u64 pll_cmp_cnt, u64 core_clk) +{ + u64 pll_cmp; + u64 rem; + + pll_cmp = pll_cmp_cnt * core_clk; + rem = do_div(pll_cmp, HDMI_REF_CLOCK); + if (rem > (HDMI_REF_CLOCK >> 1)) + pll_cmp++; + pll_cmp -= 1; + + return pll_cmp; +} + +static inline u64 hdmi_8996_v3_get_pll_cmp(u64 pll_cmp_cnt, u64 fdata) +{ + u64 dividend = pll_cmp_cnt * fdata; + u64 divisor = HDMI_REF_CLOCK * 10; + u64 rem; + + rem = do_div(dividend, divisor); + if (rem > (divisor >> 1)) + dividend++; + + return dividend - 1; +} + +static int hdmi_8996_v3_get_post_div(struct hdmi_8996_v3_post_divider *pd, + u64 bclk) +{ + u32 ratio[] = {2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35}; + u32 tx_band_sel[] = {0, 1, 2, 3}; + u64 vco_freq[60]; + u64 vco, vco_optimal, half_rate_mode = 0; + int vco_optimal_index, vco_freq_index; + int i, j, k, x; + + for (i = 0; i <= 1; i++) { + vco_optimal = HDMI_VCO_MAX_FREQ; + vco_optimal_index = -1; + vco_freq_index = 0; + for (j = 0; j < 15; j++) { + for (k = 0; k < 4; k++) { + u64 ratio_mult = ratio[j] << tx_band_sel[k]; + + vco = bclk >> half_rate_mode; + vco *= ratio_mult; + vco_freq[vco_freq_index++] = vco; + } + } + + for (x = 0; x < 60; x++) { + u64 vco_tmp = vco_freq[x]; + + if ((vco_tmp >= HDMI_VCO_MIN_FREQ) && + (vco_tmp <= vco_optimal)) { + vco_optimal = vco_tmp; + vco_optimal_index = x; + } + } + + if (vco_optimal_index == -1) { + if (!half_rate_mode) + half_rate_mode++; + else + return -EINVAL; + } else { + pd->vco_freq = vco_optimal; + pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4]; + pd->vco_ratio = ratio[vco_optimal_index / 4]; + break; + } + } + + switch (pd->vco_ratio) { + case 2: + pd->hsclk_divsel = 0; + break; + case 3: + pd->hsclk_divsel = 4; + break; + case 4: + pd->hsclk_divsel = 8; + break; + case 5: + pd->hsclk_divsel = 12; + break; + case 6: + pd->hsclk_divsel = 1; + break; + case 9: + pd->hsclk_divsel = 5; + break; + case 10: + pd->hsclk_divsel = 2; + break; + case 12: + pd->hsclk_divsel = 9; + break; + case 14: + pd->hsclk_divsel = 3; + break; + case 15: + pd->hsclk_divsel = 13; + break; + case 20: + pd->hsclk_divsel = 10; + break; + case 21: + pd->hsclk_divsel = 7; + break; + case 25: + pd->hsclk_divsel = 14; + break; + case 28: + pd->hsclk_divsel = 11; + break; + case 35: + pd->hsclk_divsel = 15; + break; + }; + + return 0; +} + +static int hdmi_8996_v1_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + u64 fdata, clk_divtx, tmds_clk; + u64 bclk; + u64 post_div_gt_2g; + u64 post_div_lt_2g; + u64 coreclk_div1_lt_2g; + u64 core_clk_div_ratio; + u64 core_clk; + u64 pll_cmp; + u64 tx_band; + u64 tx_band_div_ratio; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_tune; + u64 vco_freq; + u64 rem; + + /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = bclk/4; + else + tmds_clk = bclk; + + post_div_lt_2g = hdmi_8996_v1_get_post_div_lt_2g(bclk); + if (post_div_lt_2g == HDMI_64B_ERR_VAL) + goto fail; + + coreclk_div1_lt_2g = hdmi_8996_get_coreclk_div_lt_2g(bclk); + + core_clk_div_ratio = hdmi_8996_get_coreclk_div_ratio( + HDMI_CLKS_PLL_DIVSEL, HDMI_CORECLK_DIV); + + tx_band = hdmi_8996_v1_get_tx_band(bclk); + if (tx_band == HDMI_64B_ERR_VAL) + goto fail; + + tx_band_div_ratio = 1 << tx_band; + + if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) { + fdata = bclk; + hsclk = hdmi_8996_v1_get_hsclk(fdata); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL; + if (post_div_gt_2g == HDMI_64B_ERR_VAL) + goto fail; + + vco_freq = bclk * (post_div_gt_2g * tx_band_div_ratio); + clk_divtx = vco_freq; + do_div(clk_divtx, post_div_gt_2g); + } else { + vco_freq = bclk * (post_div_lt_2g * tx_band_div_ratio); + fdata = vco_freq; + do_div(fdata, post_div_lt_2g); + hsclk = hdmi_8996_v1_get_hsclk(fdata); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + clk_divtx = vco_freq; + do_div(clk_divtx, post_div_lt_2g); + post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL; + if (post_div_gt_2g == HDMI_64B_ERR_VAL) + goto fail; + } + + /* Decimal and fraction values */ + dec_start = fdata * post_div_gt_2g; + do_div(dec_start, pll_divisor); + frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) - + (fdata * post_div_gt_2g))) * (1 << 20)); + rem = do_div(frac_start, pll_divisor); + /* Round off frac_start to closest integer */ + if (rem >= (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false); + vco_tune = hdmi_8996_get_vco_tune(fdata, post_div_gt_2g); + + core_clk = clk_divtx; + do_div(core_clk, core_clk_div_ratio); + pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk); + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_lane_mode = 0x3; + cfg->tx_l2_lane_mode = 0x3; + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + cfg->tx_l0_res_code_lane_tx = 0x33; + cfg->tx_l1_res_code_lane_tx = 0x33; + cfg->tx_l2_res_code_lane_tx = 0x33; + cfg->tx_l3_res_code_lane_tx = 0x33; + cfg->com_restrim_ctrl = 0x0; + cfg->com_vco_tune_ctrl = 0x1C; + + cfg->com_svs_mode_clk_sel = + (bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2); + cfg->com_hsclk_sel = (0x28 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4)); + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->com_restrim_ctrl = 0x0; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->com_restrim_ctrl = 0x0; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + cfg->com_restrim_ctrl = 0xD8; + } + + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl); + + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n", + cfg->tx_l0_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n", + cfg->tx_l1_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n", + cfg->tx_l2_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n", + cfg->tx_l3_res_code_lane_tx); + + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_v2_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + u64 fdata, clk_divtx, tmds_clk; + u64 bclk; + u64 post_div; + u64 core_clk_div; + u64 core_clk_div_ratio; + u64 core_clk; + u64 pll_cmp; + u64 tx_band; + u64 tx_band_div_ratio; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_tune; + u64 vco_freq; + u64 vco_range; + u64 rem; + + /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = pix_clk >> 2; + else + tmds_clk = pix_clk; + + vco_range = bclk < HDMI_282MHZ_BIT_CLK_HZ ? HDMI_2000MHZ_BIT_CLK_HZ : + HDMI_2250MHZ_BIT_CLK_HZ; + + fdata = hdmi_8996_v2_get_fdata(bclk, vco_range); + if (fdata == HDMI_64B_ERR_VAL) + goto fail; + + hsclk = hdmi_8996_v2_get_hsclk(fdata, vco_range); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + if (bclk >= vco_range) + post_div = hdmi_8996_v2_get_post_div_gt_2g(hsclk); + else + post_div = hdmi_8996_v2_get_post_div_lt_2g(bclk, vco_range); + + if (post_div == HDMI_64B_ERR_VAL) + goto fail; + + core_clk_div = 5; + core_clk_div_ratio = core_clk_div * 2; + + tx_band = hdmi_8996_v2_get_tx_band(bclk, vco_range); + if (tx_band == HDMI_64B_ERR_VAL) + goto fail; + + tx_band_div_ratio = 1 << tx_band; + + vco_freq = hdmi_8996_v2_get_vco_freq(bclk, vco_range); + clk_divtx = vco_freq; + do_div(clk_divtx, post_div); + + /* Decimal and fraction values */ + dec_start = fdata * post_div; + do_div(dec_start, pll_divisor); + frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) - + (fdata * post_div))) * (1 << 20)); + rem = do_div(frac_start, pll_divisor); + /* Round off frac_start to closest integer */ + if (rem >= (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false); + vco_tune = hdmi_8996_get_vco_tune(fdata, post_div); + + core_clk = clk_divtx; + do_div(core_clk, core_clk_div_ratio); + pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk); + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_lane_mode = 0x3; + cfg->tx_l2_lane_mode = 0x3; + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) + cfg->com_svs_mode_clk_sel = 1; + else + cfg->com_svs_mode_clk_sel = 2; + + cfg->com_hsclk_sel = (0x28 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4)); + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + cfg->com_vco_tune_ctrl = 0x0; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->tx_l0_res_code_lane_tx = 0x3F; + cfg->tx_l1_res_code_lane_tx = 0x3F; + cfg->tx_l2_res_code_lane_tx = 0x3F; + cfg->tx_l3_res_code_lane_tx = 0x3F; + cfg->com_restrim_ctrl = 0x0; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->tx_l0_res_code_lane_tx = 0x39; + cfg->tx_l1_res_code_lane_tx = 0x39; + cfg->tx_l2_res_code_lane_tx = 0x39; + cfg->tx_l3_res_code_lane_tx = 0x39; + cfg->com_restrim_ctrl = 0x0; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + cfg->tx_l0_res_code_lane_tx = 0x3F; + cfg->tx_l1_res_code_lane_tx = 0x3F; + cfg->tx_l2_res_code_lane_tx = 0x3F; + cfg->tx_l3_res_code_lane_tx = 0x3F; + cfg->com_restrim_ctrl = 0xD8; + } + + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_vco_tune_ctrl = 0x%x\n", + cfg->com_vco_tune_ctrl); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n", + cfg->tx_l0_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n", + cfg->tx_l1_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n", + cfg->tx_l2_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n", + cfg->tx_l3_res_code_lane_tx); + DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl); + + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_v3_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + struct hdmi_8996_v3_post_divider pd; + u64 fdata, tmds_clk; + u64 bclk; + u64 pll_cmp; + u64 tx_band; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_freq; + u64 rem; + + /* FDATA, HSCLK, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = pix_clk >> 2; + else + tmds_clk = pix_clk; + + if (hdmi_8996_v3_get_post_div(&pd, bclk) || pd.vco_ratio <= 0 || + pd.vco_freq <= 0) + goto fail; + + vco_freq = pd.vco_freq; + fdata = pd.vco_freq; + do_div(fdata, pd.vco_ratio); + + hsclk = pd.hsclk_divsel; + dec_start = vco_freq; + do_div(dec_start, pll_divisor); + + frac_start = vco_freq * (1 << 20); + rem = do_div(frac_start, pll_divisor); + frac_start -= dec_start * (1 << 20); + if (rem > (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_v3_get_integloop_gain(frac_start, bclk, + false); + pll_cmp = hdmi_8996_v3_get_pll_cmp(1024, fdata); + tx_band = pd.tx_band_sel; + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) + cfg->com_svs_mode_clk_sel = 1; + else + cfg->com_svs_mode_clk_sel = 2; + + cfg->com_hsclk_sel = (0x20 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_lock_cmp_en = 0x04; + cfg->com_core_clk_en = 0x2C; + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + cfg->com_vco_tune_ctrl = 0x0; + + cfg->tx_l0_lane_mode = 0x43; + cfg->tx_l2_lane_mode = 0x43; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + } + + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg, u32 ver) +{ + switch (ver) { + case HDMI_VERSION_8996_V3: + case HDMI_VERSION_8996_V3_1_8: + return hdmi_8996_v3_calculate(pix_clk, cfg); + case HDMI_VERSION_8996_V2: + return hdmi_8996_v2_calculate(pix_clk, cfg); + default: + return hdmi_8996_v1_calculate(pix_clk, cfg); + } +} + +static int hdmi_8996_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk, u32 ver) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + struct hdmi_8996_phy_pll_reg_cfg cfg = {0}; + + rc = hdmi_8996_calculate(tmds_clk, &cfg, ver); + if (rc) { + DEV_ERR("%s: PLL calculation failed\n", __func__); + return rc; + } + + /* Initially shut down PHY */ + DEV_DBG("%s: Disabling PHY\n", __func__); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0); + udelay(500); + + /* Power up sequence */ + switch (ver) { + case HDMI_VERSION_8996_V2: + case HDMI_VERSION_8996_V3: + case HDMI_VERSION_8996_V3_1_8: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x04); + break; + }; + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX0_TX1_LANE_CTL, 0x0F); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX2_TX3_LANE_CTL, 0x0F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_LANE_MODE, cfg.tx_l0_lane_mode); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_LANE_MODE, cfg.tx_l2_lane_mode); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l0_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l1_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l2_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l3_tx_band); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0E); + if (ver == HDMI_VERSION_8996_V1) + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06); + + /* Bypass VCO calibration */ + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, + cfg.com_svs_mode_clk_sel); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_TRIM, 0x0F); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IVCO, 0x0F); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_CTRL, + cfg.com_vco_tune_ctrl); + + switch (ver) { + case HDMI_VERSION_8996_V1: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, + cfg.com_svs_mode_clk_sel); + break; + default: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06); + } + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_SELECT, 0x30); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_HSCLK_SEL, + cfg.com_hsclk_sel); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_EN, + cfg.com_lock_cmp_en); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, + cfg.com_pll_cctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, + cfg.com_pll_rctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CP_CTRL_MODE0, + cfg.com_cp_ctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START_MODE0, + cfg.com_dec_start_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1_MODE0, + cfg.com_div_frac_start1_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2_MODE0, + cfg.com_div_frac_start2_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3_MODE0, + cfg.com_div_frac_start3_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, + cfg.com_integloop_gain0_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, + cfg.com_integloop_gain1_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0, + cfg.com_lock_cmp1_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0, + cfg.com_lock_cmp2_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0, + cfg.com_lock_cmp3_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_EN, + cfg.com_core_clk_en); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORECLK_DIV, + cfg.com_coreclk_div); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_CONFIG, 0x02); + + if (ver == HDMI_VERSION_8996_V3 || ver == HDMI_VERSION_8996_V3_1_8) + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESCODE_DIV_NUM, 0x15); + + /* TX lanes setup (TX 0/1/2/3) */ + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l0_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l0_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l1_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l1_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l2_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l2_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000020); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l3_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l3_tx_emp_post1_lvl); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l0_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l0_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l1_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l1_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l2_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l2_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l3_vmode_ctrl1); + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + 0x0000000D); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l3_vmode_ctrl2); + } + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + + if (ver < HDMI_VERSION_8996_V3) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l0_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l1_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l2_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l3_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESTRIM_CTRL, + cfg.com_restrim_ctrl); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG1, 0x05); + } + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, cfg.phy_mode); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x03); + + if (ver == HDMI_VERSION_8996_V2) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01); + } + /* + * Ensure that vco configuration gets flushed to hardware before + * enabling the PLL + */ + wmb(); + return 0; +} + +static int hdmi_8996_phy_ready_status(struct mdss_pll_resources *io) +{ + u32 status = 0; + int phy_ready = 0; + int rc; + u32 read_count = 0; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return rc; + } + + DEV_DBG("%s: Waiting for PHY Ready\n", __func__); + + /* Poll for PHY read status */ + while (read_count < HDMI_PLL_POLL_MAX_READS) { + status = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS); + if ((status & BIT(0)) == 1) { + phy_ready = 1; + DEV_DBG("%s: PHY READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == HDMI_PLL_POLL_MAX_READS) { + phy_ready = 0; + DEV_DBG("%s: PHY READY TIMEOUT\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + return phy_ready; +} + +static int hdmi_8996_pll_lock_status(struct mdss_pll_resources *io) +{ + u32 status; + int pll_locked = 0; + int rc; + u32 read_count = 0; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return rc; + } + + DEV_DBG("%s: Waiting for PLL lock\n", __func__); + + while (read_count < HDMI_PLL_POLL_MAX_READS) { + status = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_C_READY_STATUS); + if ((status & BIT(0)) == 1) { + pll_locked = 1; + DEV_DBG("%s: C READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == HDMI_PLL_POLL_MAX_READS) { + pll_locked = 0; + DEV_DBG("%s: C READY TIMEOUT\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + return pll_locked; +} + +static int hdmi_8996_v1_perform_sw_calibration(struct clk *c) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + u32 max_code = 0x190; + u32 min_code = 0x0; + u32 max_cnt = 0; + u32 min_cnt = 0; + u32 expected_counter_value = 0; + u32 step = 0; + u32 dbus_all = 0; + u32 dbus_sel = 0; + u32 vco_code = 0; + u32 val = 0; + + vco_code = 0xC8; + + DEV_DBG("%s: Starting SW calibration with vco_code = %d\n", __func__, + vco_code); + + expected_counter_value = + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0) << 16) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0) << 8) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0)); + + DEV_DBG("%s: expected_counter_value = %d\n", __func__, + expected_counter_value); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(4); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(3); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x4); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val |= BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + while (1) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0, + vco_code & 0xFF); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0, + (vco_code >> 8) & 0x3); + + udelay(20); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val &= ~BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val |= BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + dbus_all = + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS3) << 24) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS2) << 16) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS1) << 8) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS0)); + + dbus_sel = (dbus_all >> 9) & 0x3FFFF; + DEV_DBG("%s: loop[%d], dbus_all = 0x%x, dbus_sel = 0x%x\n", + __func__, step, dbus_all, dbus_sel); + if (dbus_sel == 0) + DEV_ERR("%s: CHECK HDMI REF CLK\n", __func__); + + if (dbus_sel == expected_counter_value) { + max_code = vco_code; + max_cnt = dbus_sel; + min_code = vco_code; + min_cnt = dbus_sel; + } else if (dbus_sel == 0) { + max_code = vco_code; + max_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } else if (dbus_sel > expected_counter_value) { + min_code = vco_code; + min_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } else if (dbus_sel < expected_counter_value) { + max_code = vco_code; + max_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } + + step++; + + if ((vco_code == 0) || (vco_code == 0x3FF) || (step > 0x3FF)) { + DEV_ERR("%s: VCO tune code search failed\n", __func__); + rc = -ENOTSUPP; + break; + } + if ((max_code - min_code) <= 1) { + if ((max_code - min_code) == 1) { + if (abs((int)(max_cnt - expected_counter_value)) + < abs((int)(min_cnt - expected_counter_value + ))) { + vco_code = max_code; + } else { + vco_code = min_code; + } + } + break; + } + DEV_DBG("%s: loop[%d], new vco_code = %d\n", __func__, step, + vco_code); + } + + DEV_DBG("%s: CALIB done. vco_code = %d\n", __func__, vco_code); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0, + vco_code & 0xFF); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0, + (vco_code >> 8) & 0x3); + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val &= ~BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(4); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val &= ~BIT(3); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + return rc; +} + +static int hdmi_8996_v2_perform_sw_calibration(struct clk *c) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + u32 vco_code1, vco_code2, integral_loop, ready_poll; + u32 read_count = 0; + + while (read_count < (HDMI_PLL_POLL_MAX_READS << 1)) { + ready_poll = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_C_READY_STATUS); + if ((ready_poll & BIT(0)) == 1) { + ready_poll = 1; + DEV_DBG("%s: C READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == (HDMI_PLL_POLL_MAX_READS << 1)) { + ready_poll = 0; + DEV_DBG("%s: C READY TIMEOUT, TRYING SW CALIBRATION\n", + __func__); + } + + vco_code1 = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_PLLCAL_CODE1_STATUS); + vco_code2 = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_PLLCAL_CODE2_STATUS); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x5); + integral_loop = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DEBUG_BUS0); + + if (((ready_poll & 0x1) == 0) || (((ready_poll & 1) == 1) && + (vco_code1 == 0xFF) && ((vco_code2 & 0x3) == 0x1) && + (integral_loop > 0xC0))) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x04); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x00); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x17); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x11); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + } + return rc; +} + +static int hdmi_8996_perform_sw_calibration(struct clk *c, u32 ver) +{ + switch (ver) { + case HDMI_VERSION_8996_V1: + return hdmi_8996_v1_perform_sw_calibration(c); + case HDMI_VERSION_8996_V2: + return hdmi_8996_v2_perform_sw_calibration(c); + } + return 0; +} + +static int hdmi_8996_vco_enable(struct clk *c, u32 ver) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x1); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + udelay(100); + + rc = hdmi_8996_perform_sw_calibration(c, ver); + if (rc) { + DEV_ERR("%s: software calibration failed\n", __func__); + return rc; + } + + rc = hdmi_8996_pll_lock_status(io); + if (!rc) { + DEV_ERR("%s: PLL not locked\n", __func__); + return rc; + } + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + + /* Disable SSC */ + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER1, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER2, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE1, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE2, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x2); + + rc = hdmi_8996_phy_ready_status(io); + if (!rc) { + DEV_ERR("%s: PHY not READY\n", __func__); + return rc; + } + + /* Restart the retiming buffer */ + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x18); + udelay(1); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + + io->pll_on = true; + return 0; +} + +static int hdmi_8996_v1_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3_1_8); +} + +static int hdmi_8996_vco_get_lock_range(struct clk *c, unsigned long pixel_clk) +{ + u32 rng = 64, cmp_cnt = 1024; + u32 coreclk_div = 5, clks_pll_divsel = 2; + u32 vco_freq, vco_ratio, ppm_range; + u64 bclk; + struct hdmi_8996_v3_post_divider pd; + + bclk = ((u64)pixel_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + DEV_DBG("%s: rate=%ld\n", __func__, pixel_clk); + + if (hdmi_8996_v3_get_post_div(&pd, bclk) || + pd.vco_ratio <= 0 || pd.vco_freq <= 0) { + DEV_ERR("%s: couldn't get post div\n", __func__); + return -EINVAL; + } + + do_div(pd.vco_freq, HDMI_KHZ_TO_HZ * HDMI_KHZ_TO_HZ); + + vco_freq = (u32) pd.vco_freq; + vco_ratio = (u32) pd.vco_ratio; + + DEV_DBG("%s: freq %d, ratio %d\n", __func__, + vco_freq, vco_ratio); + + ppm_range = (rng * HDMI_REF_CLOCK) / cmp_cnt; + ppm_range /= vco_freq / vco_ratio; + ppm_range *= coreclk_div * clks_pll_divsel; + + DEV_DBG("%s: ppm range: %d\n", __func__, ppm_range); + + return ppm_range; +} + +static int hdmi_8996_vco_rate_atomic_update(struct clk *c, + unsigned long rate, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + void __iomem *pll; + struct hdmi_8996_phy_pll_reg_cfg cfg = {0}; + int rc = 0; + + rc = hdmi_8996_calculate(rate, &cfg, ver); + if (rc) { + DEV_ERR("%s: PLL calculation failed\n", __func__); + goto end; + } + + pll = io->pll_base; + + MDSS_PLL_REG_W(pll, QSERDES_COM_DEC_START_MODE0, + cfg.com_dec_start_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START1_MODE0, + cfg.com_div_frac_start1_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START2_MODE0, + cfg.com_div_frac_start2_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START3_MODE0, + cfg.com_div_frac_start3_mode0); + + MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x01); + MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x00); + + DEV_DBG("%s: updated to rate %ld\n", __func__, rate); +end: + return rc; +} + +static int hdmi_8996_vco_set_rate(struct clk *c, unsigned long rate, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + unsigned int set_power_dwn = 0; + bool atomic_update = false; + int rc, pll_lock_range; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("pll resource can't be enabled\n"); + return rc; + } + + DEV_DBG("%s: rate %ld\n", __func__, rate); + + if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0) && + MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) { + pll_lock_range = hdmi_8996_vco_get_lock_range(c, vco->rate); + + if (pll_lock_range > 0 && vco->rate) { + u32 range_limit; + + range_limit = vco->rate * + (pll_lock_range / HDMI_KHZ_TO_HZ); + range_limit /= HDMI_KHZ_TO_HZ; + + DEV_DBG("%s: range limit %d\n", __func__, range_limit); + + if (abs(rate - vco->rate) < range_limit) + atomic_update = true; + } + } + + if (io->pll_on && !atomic_update) + set_power_dwn = 1; + + if (atomic_update) { + hdmi_8996_vco_rate_atomic_update(c, rate, ver); + } else { + rc = hdmi_8996_phy_pll_set_clk_rate(c, rate, ver); + if (rc) + DEV_ERR("%s: Failed to set clk rate\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + if (set_power_dwn) + hdmi_8996_vco_enable(c, ver); + + vco->rate = rate; + vco->rate_set = true; + + return 0; +} + +static int hdmi_8996_v1_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3_1_8); +} + +static unsigned long hdmi_get_hsclk_sel_divisor(unsigned long hsclk_sel) +{ + unsigned long divisor; + + switch (hsclk_sel) { + case 0: + divisor = 2; + break; + case 1: + divisor = 6; + break; + case 2: + divisor = 10; + break; + case 3: + divisor = 14; + break; + case 4: + divisor = 3; + break; + case 5: + divisor = 9; + break; + case 6: + case 13: + divisor = 15; + break; + case 7: + divisor = 21; + break; + case 8: + divisor = 4; + break; + case 9: + divisor = 12; + break; + case 10: + divisor = 20; + break; + case 11: + divisor = 28; + break; + case 12: + divisor = 5; + break; + case 14: + divisor = 25; + break; + case 15: + divisor = 35; + break; + default: + divisor = 1; + DEV_ERR("%s: invalid hsclk_sel value = %lu", + __func__, hsclk_sel); + break; + } + + return divisor; +} + +static unsigned long hdmi_8996_vco_get_rate(struct clk *c) +{ + unsigned long freq = 0, hsclk_sel = 0, tx_band = 0, dec_start = 0, + div_frac_start = 0, vco_clock_freq = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (mdss_pll_resource_enable(io, true)) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return freq; + } + + dec_start = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEC_START_MODE0); + + div_frac_start = + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START1_MODE0) | + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START2_MODE0) << 8 | + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START3_MODE0) << 16; + + vco_clock_freq = (dec_start + (div_frac_start / (1 << 20))) + * 4 * (HDMI_REF_CLOCK); + + hsclk_sel = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_HSCLK_SEL) & 0x15; + hsclk_sel = hdmi_get_hsclk_sel_divisor(hsclk_sel); + tx_band = MDSS_PLL_REG_R(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND) & 0x3; + + freq = vco_clock_freq / (10 * hsclk_sel * (1 << tx_band)); + + mdss_pll_resource_enable(io, false); + + DEV_DBG("%s: freq = %lu\n", __func__, freq); + + return freq; +} + +static long hdmi_8996_vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + + DEV_DBG("rrate=%ld\n", rrate); + + return rrate; +} + +static int hdmi_8996_vco_prepare(struct clk *c, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + int ret = 0; + + DEV_DBG("rate=%ld\n", vco->rate); + + if (!vco->rate_set && vco->rate) + ret = hdmi_8996_vco_set_rate(c, vco->rate, ver); + + if (!ret) { + ret = mdss_pll_resource_enable(io, true); + if (ret) + DEV_ERR("pll resource can't be enabled\n"); + } + + return ret; +} + +static int hdmi_8996_v1_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3_1_8); +} + +static void hdmi_8996_vco_unprepare(struct clk *c) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + vco->rate_set = false; + + if (!io) { + DEV_ERR("Invalid input parameter\n"); + return; + } + + if (!io->pll_on && + mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return; + } + + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + io->pll_on = false; +} + +static enum handoff hdmi_8996_vco_handoff(struct clk *c) +{ + enum handoff ret = HANDOFF_DISABLED_CLK; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (is_gdsc_disabled(io)) + return HANDOFF_DISABLED_CLK; + + if (mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return ret; + } + + io->handoff_resources = true; + + if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0)) { + if (MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) { + io->pll_on = true; + c->rate = hdmi_8996_vco_get_rate(c); + vco->rate = c->rate; + ret = HANDOFF_ENABLED_CLK; + } else { + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + DEV_DBG("%s: PHY not ready\n", __func__); + } + } else { + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + DEV_DBG("%s: PLL not locked\n", __func__); + } + + DEV_DBG("done, ret=%d\n", ret); + return ret; +} + +static struct clk_ops hdmi_8996_v1_vco_clk_ops = { + .enable = hdmi_8996_v1_vco_enable, + .set_rate = hdmi_8996_v1_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v1_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v2_vco_clk_ops = { + .enable = hdmi_8996_v2_vco_enable, + .set_rate = hdmi_8996_v2_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v2_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v3_vco_clk_ops = { + .enable = hdmi_8996_v3_vco_enable, + .set_rate = hdmi_8996_v3_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v3_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = { + .enable = hdmi_8996_v3_1p8_vco_enable, + .set_rate = hdmi_8996_v3_1p8_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v3_1p8_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + + +static struct hdmi_pll_vco_clk hdmi_vco_clk = { + .c = { + .dbg_name = "hdmi_8996_vco_clk", + .ops = &hdmi_8996_v1_vco_clk_ops, + CLK_INIT(hdmi_vco_clk.c), + }, +}; + +static struct clk_lookup hdmipllcc_8996[] = { + CLK_LIST(hdmi_vco_clk), +}; + +int hdmi_8996_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res, u32 ver) +{ + int rc = -ENOTSUPP; + + if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) { + DEV_ERR("%s: Invalid input parameters\n", __func__); + return -EPROBE_DEFER; + } + + /* Set client data for vco, mux and div clocks */ + hdmi_vco_clk.priv = pll_res; + + switch (ver) { + case HDMI_VERSION_8996_V2: + hdmi_vco_clk.c.ops = &hdmi_8996_v2_vco_clk_ops; + break; + case HDMI_VERSION_8996_V3: + hdmi_vco_clk.c.ops = &hdmi_8996_v3_vco_clk_ops; + break; + case HDMI_VERSION_8996_V3_1_8: + hdmi_vco_clk.c.ops = &hdmi_8996_v3_1p8_vco_clk_ops; + break; + default: + hdmi_vco_clk.c.ops = &hdmi_8996_v1_vco_clk_ops; + break; + }; + + rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8996, + ARRAY_SIZE(hdmipllcc_8996)); + if (rc) { + DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc); + rc = -EPROBE_DEFER; + } else { + DEV_DBG("%s SUCCESS\n", __func__); + } + + return rc; +} + +int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V1); +} + +int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V2); +} + +int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V3); +} + +int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V3_1_8); +} diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll.h b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h new file mode 100644 index 000000000000..d4226bf43e13 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_HDMI_PLL_H +#define __MDSS_HDMI_PLL_H + +struct hdmi_pll_cfg { + unsigned long vco_rate; + u32 reg; +}; + +struct hdmi_pll_vco_clk { + unsigned long rate; /* current vco rate */ + unsigned long min_rate; /* min vco rate */ + unsigned long max_rate; /* max vco rate */ + bool rate_set; + struct hdmi_pll_cfg *ip_seti; + struct hdmi_pll_cfg *cp_seti; + struct hdmi_pll_cfg *ip_setp; + struct hdmi_pll_cfg *cp_setp; + struct hdmi_pll_cfg *crctrl; + void *priv; + + struct clk c; +}; + +static inline struct hdmi_pll_vco_clk *to_hdmi_vco_clk(struct clk *clk) +{ + return container_of(clk, struct hdmi_pll_vco_clk, c); +} + +int hdmi_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_20nm_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_cobalt_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +#endif diff --git a/drivers/clk/qcom/mdss/mdss-pll-util.c b/drivers/clk/qcom/mdss/mdss-pll-util.c new file mode 100644 index 000000000000..690c53f30eb7 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll-util.c @@ -0,0 +1,438 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk/msm-clock-generic.h> +#include <linux/of_address.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/memblock.h> + +#include "mdss-pll.h" + +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + rc = msm_dss_config_vreg(&pdev->dev, + mp->vreg_config, mp->num_vreg, 1); + if (rc) { + pr_err("Vreg config failed rc=%d\n", rc); + goto vreg_err; + } + + rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk); + if (rc) { + pr_err("Clock get failed rc=%d\n", rc); + goto clk_err; + } + + return rc; + +clk_err: + msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +/** + * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name + *@pll_res: Pointer to the PLL resource + *@name: Regulator name as specified in the pll dtsi + * + * This is a helper function to retrieve the regulator information + * for each pll resource. + */ +struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res + , char *name) +{ + + struct dss_vreg *regulator = NULL; + int i; + + if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) { + pr_err("%s Invalid PLL resource\n", __func__); + goto error; + } + + regulator = pll_res->mp.vreg_config; + + for (i = 0; i < pll_res->mp.num_vreg; i++) { + if (!strcmp(name, regulator->vreg_name)) { + pr_debug("Found regulator match for %s\n", name); + break; + } + regulator++; + } + +error: + return regulator; +} + +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + struct dss_module_power *mp = &pll_res->mp; + + msm_dss_put_clk(mp->clk_config, mp->num_clk); + + msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); +} + +void mdss_pll_util_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + struct dss_module_power *mp = &pll_res->mp; + + devm_kfree(&pdev->dev, mp->clk_config); + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; + mp->num_clk = 0; +} + +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + if (enable) { + rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); + if (rc) { + pr_err("Failed to enable vregs rc=%d\n", rc); + goto vreg_err; + } + + rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk); + if (rc) { + pr_err("Failed to set clock rate rc=%d\n", rc); + goto clk_err; + } + + rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); + if (rc) { + pr_err("clock enable failed rc:%d\n", rc); + goto clk_err; + } + } else { + msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); + + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); + } + + return rc; + +clk_err: + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_root_node = NULL; + struct device_node *supply_node = NULL; + struct dss_module_power *mp = &pll_res->mp; + + of_node = pdev->dev.of_node; + + mp->num_vreg = 0; + supply_root_node = of_get_child_by_name(of_node, + "qcom,platform-supply-entries"); + if (!supply_root_node) { + pr_err("no supply entry present\n"); + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + mp->num_vreg++; + } + + if (mp->num_vreg == 0) { + pr_debug("no vreg\n"); + return rc; + } + pr_debug("vreg found. count=%d\n", mp->num_vreg); + + mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + rc = -ENOMEM; + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + + const char *st = NULL; + + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err(":error reading name. rc=%d\n", rc); + goto error; + } + + strlcpy(mp->vreg_config[i].vreg_name, st, + sizeof(mp->vreg_config[i].vreg_name)); + + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err(": error reading min volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err(": error reading max volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err(": error reading enable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].enable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err(": error reading disable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].disable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0); + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + mp->vreg_config[i].vreg_name, + mp->vreg_config[i].min_voltage, + mp->vreg_config[i].max_voltage, + mp->vreg_config[i].enable_load, + mp->vreg_config[i].disable_load, + mp->vreg_config[i].pre_on_sleep, + mp->vreg_config[i].post_on_sleep, + mp->vreg_config[i].pre_off_sleep, + mp->vreg_config[i].post_off_sleep); + ++i; + + rc = 0; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(&pdev->dev, mp->vreg_config); + mp->vreg_config = NULL; + mp->num_vreg = 0; + } + + return rc; +} + +static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + u32 i = 0, rc = 0; + struct dss_module_power *mp = &pll_res->mp; + const char *clock_name; + u32 clock_rate; + + mp->num_clk = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (mp->num_clk <= 0) { + pr_err("clocks are not defined\n"); + goto clk_err; + } + + mp->clk_config = devm_kzalloc(&pdev->dev, + sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL); + if (!mp->clk_config) { + rc = -ENOMEM; + mp->num_clk = 0; + goto clk_err; + } + + for (i = 0; i < mp->num_clk; i++) { + of_property_read_string_index(pdev->dev.of_node, "clock-names", + i, &clock_name); + strlcpy(mp->clk_config[i].clk_name, clock_name, + sizeof(mp->clk_config[i].clk_name)); + + of_property_read_u32_index(pdev->dev.of_node, "clock-rate", + i, &clock_rate); + mp->clk_config[i].rate = clock_rate; + + if (!clock_rate) + mp->clk_config[i].type = DSS_CLK_AHB; + else + mp->clk_config[i].type = DSS_CLK_PCLK; + } + +clk_err: + return rc; +} + +static void mdss_pll_free_bootmem(u32 mem_addr, u32 size) +{ + unsigned long pfn_start, pfn_end, pfn_idx; + + pfn_start = mem_addr >> PAGE_SHIFT; + pfn_end = (mem_addr + size) >> PAGE_SHIFT; + for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++) + free_reserved_page(pfn_to_page(pfn_idx)); +} + +static int mdss_pll_util_parse_dt_dfps(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct device_node *pnode; + const u32 *addr; + struct vm_struct *area; + u64 size; + u32 offsets[2]; + unsigned long virt_add; + + pnode = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (IS_ERR_OR_NULL(pnode)) { + rc = PTR_ERR(pnode); + goto pnode_err; + } + + addr = of_get_address(pnode, 0, &size, NULL); + if (!addr) { + pr_err("failed to parse the dfps memory address\n"); + rc = -EINVAL; + goto pnode_err; + } + /* maintain compatibility for 32/64 bit */ + offsets[0] = (u32) of_read_ulong(addr, 2); + offsets[1] = (u32) size; + + area = get_vm_area(offsets[1], VM_IOREMAP); + if (!area) { + rc = -ENOMEM; + goto dfps_mem_err; + } + + virt_add = (unsigned long)area->addr; + rc = ioremap_page_range(virt_add, (virt_add + offsets[1]), + offsets[0], PAGE_KERNEL); + if (rc) { + rc = -ENOMEM; + goto ioremap_err; + } + + pll_res->dfps = kzalloc(sizeof(struct dfps_info), GFP_KERNEL); + if (IS_ERR_OR_NULL(pll_res->dfps)) { + rc = PTR_ERR(pll_res->dfps); + pr_err("couldn't allocate dfps kernel memory\n"); + goto addr_err; + } + + /* memcopy complete dfps structure from kernel virtual memory */ + memcpy_fromio(pll_res->dfps, area->addr, sizeof(struct dfps_info)); + +addr_err: + if (virt_add) + unmap_kernel_range(virt_add, (unsigned long) size); +ioremap_err: + if (area) + vfree(area->addr); +dfps_mem_err: + /* free the dfps memory here */ + memblock_free(offsets[0], offsets[1]); + mdss_pll_free_bootmem(offsets[0], offsets[1]); +pnode_err: + if (pnode) + of_node_put(pnode); + + dma_release_declared_memory(&pdev->dev); + return rc; +} + +int mdss_pll_util_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + rc = mdss_pll_util_parse_dt_supply(pdev, pll_res); + if (rc) { + pr_err("vreg parsing failed rc=%d\n", rc); + goto end; + } + + rc = mdss_pll_util_parse_dt_clock(pdev, pll_res); + if (rc) { + pr_err("clock name parsing failed rc=%d", rc); + goto clk_err; + } + + if (mdss_pll_util_parse_dt_dfps(pdev, pll_res)) + pr_err("dfps not enabled!\n"); + + return rc; + +clk_err: + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; +end: + return rc; +} diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c new file mode 100644 index 000000000000..e91e9c9dc768 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll.c @@ -0,0 +1,437 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-hdmi-pll.h" +#include "mdss-dp-pll.h" + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable) +{ + int rc = 0; + int changed = 0; + + if (!pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + /* + * Don't turn off resources during handoff or add more than + * 1 refcount. + */ + if (pll_res->handoff_resources && + (!enable || (enable & pll_res->resource_enable))) { + pr_debug("Do not turn on/off pll resources during handoff case\n"); + return rc; + } + + if (enable) { + if (pll_res->resource_ref_cnt == 0) + changed++; + pll_res->resource_ref_cnt++; + } else { + if (pll_res->resource_ref_cnt) { + pll_res->resource_ref_cnt--; + if (pll_res->resource_ref_cnt == 0) + changed++; + } else { + pr_err("PLL Resources already OFF\n"); + } + } + + if (changed) { + rc = mdss_pll_util_resource_enable(pll_res, enable); + if (rc) + pr_err("Resource update failed rc=%d\n", rc); + else + pll_res->resource_enable = enable; + } + + return rc; +} + +static int mdss_pll_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + return mdss_pll_util_resource_init(pdev, pll_res); +} + +static void mdss_pll_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_deinit(pdev, pll_res); +} + +static void mdss_pll_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_release(pdev, pll_res); +} + +static int mdss_pll_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + const char *compatible_stream; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + rc = mdss_pll_util_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Failed to parse the resources rc=%d\n", rc); + goto end; + } + + compatible_stream = of_get_property(pdev->dev.of_node, + "compatible", NULL); + if (!compatible_stream) { + pr_err("Failed to parse the compatible stream\n"); + goto err; + } + + if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_8996; + pll_res->target_id = MDSS_PLL_TARGET_8996; + pll_res->revision = 1; + } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996_v2")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_8996; + pll_res->target_id = MDSS_PLL_TARGET_8996; + pll_res->revision = 2; + } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_COBALT; + } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_DP_PLL_COBALT; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V2; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v3")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3; + } else if (!strcmp(compatible_stream, + "qcom,mdss_hdmi_pll_8996_v3_1p8")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_COBALT; + } else { + goto err; + } + + return rc; + +err: + mdss_pll_resource_release(pdev, pll_res); +end: + return rc; +} + +static int mdss_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + switch (pll_res->pll_interface_type) { + case MDSS_DSI_PLL_8996: + rc = dsi_pll_clock_register_8996(pdev, pll_res); + break; + case MDSS_DSI_PLL_COBALT: + rc = dsi_pll_clock_register_cobalt(pdev, pll_res); + case MDSS_DP_PLL_COBALT: + rc = dp_pll_clock_register_cobalt(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996: + rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V2: + rc = hdmi_8996_v2_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V3: + rc = hdmi_8996_v3_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V3_1_8: + rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_COBALT: + rc = hdmi_cobalt_pll_clock_register(pdev, pll_res); + break; + case MDSS_UNKNOWN_PLL: + default: + rc = -EINVAL; + break; + } + + if (rc) { + pr_err("Pll ndx=%d clock register failed rc=%d\n", + pll_res->index, rc); + } + + return rc; +} + +static int mdss_pll_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *label; + struct resource *pll_base_reg; + struct resource *phy_base_reg; + struct resource *dynamic_pll_base_reg; + struct resource *gdsc_base_reg; + struct mdss_pll_resources *pll_res; + + if (!pdev->dev.of_node) { + pr_err("MDSS pll driver only supports device tree probe\n"); + rc = -ENOTSUPP; + goto error; + } + + label = of_get_property(pdev->dev.of_node, "label", NULL); + if (!label) + pr_info("%d: MDSS pll label not specified\n", __LINE__); + else + pr_info("MDSS pll label = %s\n", label); + + pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources), + GFP_KERNEL); + if (!pll_res) { + rc = -ENOMEM; + goto error; + } + platform_set_drvdata(pdev, pll_res); + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", + &pll_res->index); + if (rc) { + pr_err("Unable to get the cell-index rc=%d\n", rc); + pll_res->index = 0; + } + + pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node, + "qcom,dsi-pll-ssc-en"); + + if (pll_res->ssc_en) { + pr_info("%s: label=%s PLL SSC enabled\n", __func__, label); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,ssc-frequency-hz", &pll_res->ssc_freq); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,ssc-ppm", &pll_res->ssc_ppm); + + pll_res->ssc_center = false; + + label = of_get_property(pdev->dev.of_node, + "qcom,dsi-pll-ssc-mode", NULL); + + if (label && !strcmp(label, "center-spread")) + pll_res->ssc_center = true; + } + + pll_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "pll_base"); + if (!pll_base_reg) { + pr_err("Unable to get the pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + pll_res->pll_base = ioremap(pll_base_reg->start, + resource_size(pll_base_reg)); + if (!pll_res->pll_base) { + pr_err("Unable to remap pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + pr_debug("%s: ndx=%d base=%p\n", __func__, + pll_res->index, pll_res->pll_base); + + rc = mdss_pll_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Pll resource parsing from dt failed rc=%d\n", rc); + goto res_parse_error; + } + + phy_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "phy_base"); + if (phy_base_reg) { + pll_res->phy_base = ioremap(phy_base_reg->start, + resource_size(phy_base_reg)); + if (!pll_res->phy_base) { + pr_err("Unable to remap pll phy base resources\n"); + rc = -ENOMEM; + goto phy_io_error; + } + } + + dynamic_pll_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "dynamic_pll_base"); + if (dynamic_pll_base_reg) { + pll_res->dyn_pll_base = ioremap(dynamic_pll_base_reg->start, + resource_size(dynamic_pll_base_reg)); + if (!pll_res->dyn_pll_base) { + pr_err("Unable to remap dynamic pll base resources\n"); + rc = -ENOMEM; + goto dyn_pll_io_error; + } + } + + gdsc_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "gdsc_base"); + if (!gdsc_base_reg) { + pr_err("Unable to get the gdsc base resource\n"); + rc = -ENOMEM; + goto gdsc_io_error; + } + pll_res->gdsc_base = ioremap(gdsc_base_reg->start, + resource_size(gdsc_base_reg)); + if (!pll_res->gdsc_base) { + pr_err("Unable to remap gdsc base resources\n"); + rc = -ENOMEM; + goto gdsc_io_error; + } + + rc = mdss_pll_resource_init(pdev, pll_res); + if (rc) { + pr_err("Pll ndx=%d resource init failed rc=%d\n", + pll_res->index, rc); + goto res_init_error; + } + + rc = mdss_pll_clock_register(pdev, pll_res); + if (rc) { + pr_err("Pll ndx=%d clock register failed rc=%d\n", + pll_res->index, rc); + goto clock_register_error; + } + + return rc; + +clock_register_error: + mdss_pll_resource_deinit(pdev, pll_res); +res_init_error: + if (pll_res->gdsc_base) + iounmap(pll_res->gdsc_base); +gdsc_io_error: + if (pll_res->dyn_pll_base) + iounmap(pll_res->dyn_pll_base); +dyn_pll_io_error: + if (pll_res->phy_base) + iounmap(pll_res->phy_base); +phy_io_error: + mdss_pll_resource_release(pdev, pll_res); +res_parse_error: + iounmap(pll_res->pll_base); +io_error: + devm_kfree(&pdev->dev, pll_res); +error: + return rc; +} + +static int mdss_pll_remove(struct platform_device *pdev) +{ + struct mdss_pll_resources *pll_res; + + pll_res = platform_get_drvdata(pdev); + if (!pll_res) { + pr_err("Invalid PLL resource data"); + return 0; + } + + mdss_pll_resource_deinit(pdev, pll_res); + if (pll_res->phy_base) + iounmap(pll_res->phy_base); + if (pll_res->gdsc_base) + iounmap(pll_res->gdsc_base); + mdss_pll_resource_release(pdev, pll_res); + iounmap(pll_res->pll_base); + devm_kfree(&pdev->dev, pll_res); + return 0; +} + +static const struct of_device_id mdss_pll_dt_match[] = { + {.compatible = "qcom,mdss_dsi_pll_8996"}, + {.compatible = "qcom,mdss_dsi_pll_8996_v2"}, + {.compatible = "qcom,mdss_dsi_pll_cobalt"}, + {.compatible = "qcom,mdss_hdmi_pll_8996"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v2"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v3"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"}, + {.compatible = "qcom,mdss_dp_pll_cobalt"}, + {.compatible = "qcom,mdss_hdmi_pll_cobalt"}, + {} +}; + +MODULE_DEVICE_TABLE(of, mdss_clock_dt_match); + +static struct platform_driver mdss_pll_driver = { + .probe = mdss_pll_probe, + .remove = mdss_pll_remove, + .driver = { + .name = "mdss_pll", + .of_match_table = mdss_pll_dt_match, + }, +}; + +static int __init mdss_pll_driver_init(void) +{ + int rc; + + rc = platform_driver_register(&mdss_pll_driver); + if (rc) + pr_err("mdss_register_pll_driver() failed!\n"); + + return rc; +} +subsys_initcall(mdss_pll_driver_init); + +static void __exit mdss_pll_driver_deinit(void) +{ + platform_driver_unregister(&mdss_pll_driver); +} +module_exit(mdss_pll_driver_deinit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("mdss pll driver"); diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h new file mode 100644 index 000000000000..a2eb03e09146 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll.h @@ -0,0 +1,233 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_PLL_H +#define __MDSS_PLL_H + +#include <linux/mdss_io_util.h> +#include <linux/clk/msm-clock-generic.h> +#include <linux/io.h> + +#define MDSS_PLL_REG_W(base, offset, data) \ + writel_relaxed((data), (base) + (offset)) +#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset)) + +#define PLL_CALC_DATA(addr0, addr1, data0, data1) \ + (((data1) << 24) | ((((addr1) / 4) & 0xFF) << 16) | \ + ((data0) << 8) | (((addr0) / 4) & 0xFF)) + +#define MDSS_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \ + writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \ + (base) + (offset)) + +enum { + MDSS_DSI_PLL_8996, + MDSS_DSI_PLL_COBALT, + MDSS_DP_PLL_COBALT, + MDSS_HDMI_PLL_8996, + MDSS_HDMI_PLL_8996_V2, + MDSS_HDMI_PLL_8996_V3, + MDSS_HDMI_PLL_8996_V3_1_8, + MDSS_HDMI_PLL_COBALT, + MDSS_UNKNOWN_PLL, +}; + +enum { + MDSS_PLL_TARGET_8996, +}; + +#define DFPS_MAX_NUM_OF_FRAME_RATES 20 + +struct dfps_panel_info { + uint32_t enabled; + uint32_t frame_rate_cnt; + uint32_t frame_rate[DFPS_MAX_NUM_OF_FRAME_RATES]; /* hz */ +}; + +struct dfps_pll_codes { + uint32_t pll_codes_1; + uint32_t pll_codes_2; +}; + +struct dfps_codes_info { + uint32_t is_valid; + uint32_t frame_rate; /* hz */ + uint32_t clk_rate; /* hz */ + struct dfps_pll_codes pll_codes; +}; + +struct dfps_info { + struct dfps_panel_info panel_dfps; + struct dfps_codes_info codes_dfps[DFPS_MAX_NUM_OF_FRAME_RATES]; + void *dfps_fb_base; +}; + +struct mdss_pll_resources { + + /* Pll specific resources like GPIO, power supply, clocks, etc*/ + struct dss_module_power mp; + + /* + * dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh + * register mapping + */ + void __iomem *pll_base; + void __iomem *phy_base; + void __iomem *gdsc_base; + void __iomem *dyn_pll_base; + + bool is_init_locked; + s64 vco_current_rate; + s64 vco_locking_rate; + s64 vco_ref_clk_rate; + + /* + * Certain pll's needs to update the same vco rate after resume in + * suspend/resume scenario. Cached the vco rate for such plls. + */ + unsigned long vco_cached_rate; + + /* dsi/edp/hmdi pll interface type */ + u32 pll_interface_type; + + /* + * Target ID. Used in pll_register API for valid target check before + * registering the PLL clocks. + */ + u32 target_id; + + /* HW recommended delay during configuration of vco clock rate */ + u32 vco_delay; + + /* Ref-count of the PLL resources */ + u32 resource_ref_cnt; + + /* + * Keep track to resource status to avoid updating same status for the + * pll from different paths + */ + bool resource_enable; + + /* + * Certain plls' do not allow vco rate update if it is on. Keep track of + * status for them to turn on/off after set rate success. + */ + bool pll_on; + + /* + * handoff_status is true of pll is already enabled by bootloader with + * continuous splash enable case. Clock API will call the handoff API + * to enable the status. It is disabled if continuous splash + * feature is disabled. + */ + bool handoff_resources; + + /* + * caching the pll trim codes in the case of dynamic refresh + */ + int cache_pll_trim_codes[2]; + + /* + * for maintaining the status of saving trim codes + */ + bool reg_upd; + + /* + * Notifier callback for MDSS gdsc regulator events + */ + struct notifier_block gdsc_cb; + + /* + * Worker function to call PLL off event + */ + struct work_struct pll_off; + + /* + * PLL index if multiple index are available. Eg. in case of + * DSI we have 2 plls. + */ + uint32_t index; + + bool ssc_en; /* share pll with master */ + bool ssc_center; /* default is down spread */ + u32 ssc_freq; + u32 ssc_ppm; + + struct mdss_pll_resources *slave; + + /* + * target pll revision information + */ + int revision; + + void *priv; + + /* + * dynamic refresh pll codes stored in this structure + */ + struct dfps_info *dfps; + +}; + +struct mdss_pll_vco_calc { + s32 div_frac_start1; + s32 div_frac_start2; + s32 div_frac_start3; + s64 dec_start1; + s64 dec_start2; + s64 pll_plllock_cmp1; + s64 pll_plllock_cmp2; + s64 pll_plllock_cmp3; +}; + +static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res) +{ + if (!pll_res->gdsc_base) { + WARN(1, "gdsc_base register is not defined\n"); + return true; + } + + return ((readl_relaxed(pll_res->gdsc_base + 0x4) & BIT(31)) && + (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true; +} + +static inline int mdss_pll_div_prepare(struct clk *c) +{ + struct div_clk *div = to_div_clk(c); + /* Restore the divider's value */ + return div->ops->set_div(div, div->data.div); +} + +static inline int mdss_set_mux_sel(struct mux_clk *clk, int sel) +{ + return 0; +} + +static inline int mdss_get_mux_sel(struct mux_clk *clk) +{ + return 0; +} + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable); +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable); +int mdss_pll_util_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res + , char *name); +#endif diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 680d0b596970..386c85fc714b 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -372,18 +372,18 @@ config CRYPTO_DEV_QCRYPTO config CRYPTO_DEV_QCOM_MSM_QCE tristate "Qualcomm Crypto Engine (QCE) module" - select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT + select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT || ARCH_MSMFALCON default n help This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660 MSM8x55, MSM8960, MSM9615, MSM8916, MSM8994, MSM8996, FSM9900, - MSMTITANINUM, APQ8084 and MSMCOBALT. + MSMTITANINUM, APQ8084, MSMCOBALT and MSMFALCON. To compile this driver as a module, choose M here: the For MSM7x30 MSM8660 and MSM8x55 the module is called qce For MSM8960, APQ8064 and MSM9615 the module is called qce40 For MSM8974, MSM8916, MSM8994, MSM8996, MSM8992, MSMTITANIUM, - APQ8084 and MSMCOBALT the module is called qce50. + APQ8084, MSMCOBALT and MSMFALCON the module is called qce50. config CRYPTO_DEV_QCEDEV tristate "QCEDEV Interface to CE module" @@ -391,7 +391,7 @@ config CRYPTO_DEV_QCEDEV help This driver supports Qualcomm QCEDEV Crypto in MSM7x30, MSM8660, MSM8960, MSM9615, APQ8064, MSM8974, MSM8916, MSM8994, MSM8996, - APQ8084, MSMCOBALT. This exposes the interface to the QCE hardware + APQ8084, MSMCOBALT, MSMFALCON. This exposes the interface to the QCE hardware accelerator via IOCTLs. To compile this driver as a module, choose M here: the diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index 6bcc08e94657..61f99370863d 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -1,6 +1,6 @@ /* Qualcomm Crypto Engine driver. * - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -69,7 +69,7 @@ static LIST_HEAD(qce50_bam_list); /* Max number of request supported */ #define MAX_QCE_BAM_REQ 8 /* Interrupt flag will be set for every SET_INTR_AT_REQ request */ -#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ - 2) +#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ / 2) /* To create extra request space to hold dummy request */ #define MAX_QCE_BAM_REQ_WITH_DUMMY_REQ (MAX_QCE_BAM_REQ + 1) /* Allocate the memory for MAX_QCE_BAM_REQ + 1 (for dummy request) */ @@ -84,6 +84,12 @@ static LIST_HEAD(qce50_bam_list); /* Index to point the dummy request */ #define DUMMY_REQ_INDEX MAX_QCE_BAM_REQ +enum qce_owner { + QCE_OWNER_NONE = 0, + QCE_OWNER_CLIENT = 1, + QCE_OWNER_TIMEOUT = 2 +}; + struct dummy_request { struct qce_sha_req sreq; uint8_t *in_buf; @@ -133,9 +139,8 @@ struct qce_device { struct ce_bam_info ce_bam_info; struct ce_request_info ce_request_info[MAX_QCE_ALLOC_BAM_REQ]; unsigned int ce_request_index; - spinlock_t lock; - spinlock_t sps_lock; - unsigned int no_of_queued_req; + enum qce_owner owner; + atomic_t no_of_queued_req; struct timer_list timer; struct dummy_request dummyreq; unsigned int mode; @@ -144,6 +149,7 @@ struct qce_device { struct qce_driver_stats qce_stats; atomic_t bunch_cmd_seq; atomic_t last_intr_seq; + bool cadence_flag; }; static void print_notify_debug(struct sps_event_notify *notify); @@ -2539,7 +2545,6 @@ static int _qce_sps_add_cmd(struct qce_device *pce_dev, uint32_t flag, static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) { int rc = 0; - unsigned long flags; struct ce_sps_data *pce_sps_data; pce_sps_data = &pce_dev->ce_request_info[req_info].ce_sps; @@ -2551,7 +2556,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) (unsigned int) req_info)); _qce_dump_descr_fifos_dbg(pce_dev, req_info); - spin_lock_irqsave(&pce_dev->sps_lock, flags); if (pce_sps_data->in_transfer.iovec_count) { rc = sps_transfer(pce_dev->ce_bam_info.consumer.pipe, &pce_sps_data->in_transfer); @@ -2570,7 +2574,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) ret: if (rc) _qce_dump_descr_fifos(pce_dev, req_info); - spin_unlock_irqrestore(&pce_dev->sps_lock, flags); return rc; } @@ -2955,23 +2958,20 @@ static inline int qce_alloc_req_info(struct qce_device *pce_dev) } } pr_warn("pcedev %d no reqs available no_of_queued_req %d\n", - pce_dev->dev_no, pce_dev->no_of_queued_req); + pce_dev->dev_no, atomic_read( + &pce_dev->no_of_queued_req)); return -EBUSY; } static inline void qce_free_req_info(struct qce_device *pce_dev, int req_info, bool is_complete) { - unsigned long flags; - - spin_lock_irqsave(&pce_dev->lock, flags); pce_dev->ce_request_info[req_info].xfer_type = QCE_XFER_TYPE_LAST; if (xchg(&pce_dev->ce_request_info[req_info].in_use, false) == true) { if (req_info < MAX_QCE_BAM_REQ && is_complete) - pce_dev->no_of_queued_req--; + atomic_dec(&pce_dev->no_of_queued_req); } else pr_warn("request info %d free already\n", req_info); - spin_unlock_irqrestore(&pce_dev->lock, flags); } static void print_notify_debug(struct sps_event_notify *notify) @@ -3018,7 +3018,6 @@ static void qce_multireq_timeout(unsigned long data) { struct qce_device *pce_dev = (struct qce_device *)data; int ret = 0; - unsigned long flags; int last_seq; last_seq = atomic_read(&pce_dev->bunch_cmd_seq); @@ -3029,27 +3028,29 @@ static void qce_multireq_timeout(unsigned long data) return; } /* last bunch mode command time out */ - spin_lock_irqsave(&pce_dev->lock, flags); + if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_TIMEOUT) + != QCE_OWNER_NONE) { + mod_timer(&(pce_dev->timer), (jiffies + DELAY_IN_JIFFIES)); + return; + } del_timer(&(pce_dev->timer)); pce_dev->mode = IN_INTERRUPT_MODE; pce_dev->qce_stats.no_of_timeouts++; pr_debug("pcedev %d mode switch to INTR\n", pce_dev->dev_no); - spin_unlock_irqrestore(&pce_dev->lock, flags); ret = qce_dummy_req(pce_dev); if (ret) pr_warn("pcedev %d: Failed to insert dummy req\n", pce_dev->dev_no); + cmpxchg(&pce_dev->owner, QCE_OWNER_TIMEOUT, QCE_OWNER_NONE); } void qce_get_driver_stats(void *handle) { - unsigned long flags; struct qce_device *pce_dev = (struct qce_device *) handle; if (!_qce50_disp_stats) return; - spin_lock_irqsave(&pce_dev->lock, flags); pr_info("Engine %d timeout occuured %d\n", pce_dev->dev_no, pce_dev->qce_stats.no_of_timeouts); pr_info("Engine %d dummy request inserted %d\n", pce_dev->dev_no, @@ -3059,20 +3060,16 @@ void qce_get_driver_stats(void *handle) else pr_info("Engine %d is in INTERRUPT MODE\n", pce_dev->dev_no); pr_info("Engine %d outstanding request %d\n", pce_dev->dev_no, - pce_dev->no_of_queued_req); - spin_unlock_irqrestore(&pce_dev->lock, flags); + atomic_read(&pce_dev->no_of_queued_req)); } EXPORT_SYMBOL(qce_get_driver_stats); void qce_clear_driver_stats(void *handle) { - unsigned long flags; struct qce_device *pce_dev = (struct qce_device *) handle; - spin_lock_irqsave(&pce_dev->lock, flags); pce_dev->qce_stats.no_of_timeouts = 0; pce_dev->qce_stats.no_of_dummy_reqs = 0; - spin_unlock_irqrestore(&pce_dev->lock, flags); } EXPORT_SYMBOL(qce_clear_driver_stats); @@ -3084,7 +3081,6 @@ static void _sps_producer_callback(struct sps_event_notify *notify) unsigned int req_info; struct ce_sps_data *pce_sps_data; struct ce_request_info *preq_info; - unsigned long flags; print_notify_debug(notify); @@ -3113,10 +3109,8 @@ static void _sps_producer_callback(struct sps_event_notify *notify) &pce_sps_data->out_transfer); _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); - spin_lock_irqsave(&pce_dev->sps_lock, flags); rc = sps_transfer(pce_dev->ce_bam_info.producer.pipe, &pce_sps_data->out_transfer); - spin_unlock_irqrestore(&pce_dev->sps_lock, flags); if (rc) { pr_err("sps_xfr() fail (producer pipe=0x%lx) rc = %d\n", (uintptr_t)pce_dev->ce_bam_info.producer.pipe, @@ -4590,18 +4584,27 @@ static int qce_dummy_req(struct qce_device *pce_dev) static int select_mode(struct qce_device *pce_dev, struct ce_request_info *preq_info) { - unsigned long flags; struct ce_sps_data *pce_sps_data = &preq_info->ce_sps; + unsigned int no_of_queued_req; + unsigned int cadence; if (!pce_dev->no_get_around) { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); return 0; } - spin_lock_irqsave(&pce_dev->lock, flags); - pce_dev->no_of_queued_req++; + /* + * claim ownership of device + */ +again: + if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_CLIENT) + != QCE_OWNER_NONE) { + ndelay(40); + goto again; + } + no_of_queued_req = atomic_inc_return(&pce_dev->no_of_queued_req); if (pce_dev->mode == IN_INTERRUPT_MODE) { - if (pce_dev->no_of_queued_req >= MAX_BUNCH_MODE_REQ) { + if (no_of_queued_req >= MAX_BUNCH_MODE_REQ) { pce_dev->mode = IN_BUNCH_MODE; pr_debug("pcedev %d mode switch to BUNCH\n", pce_dev->dev_no); @@ -4618,17 +4621,21 @@ static int select_mode(struct qce_device *pce_dev, } } else { pce_dev->intr_cadence++; - if (pce_dev->intr_cadence >= SET_INTR_AT_REQ) { + cadence = (preq_info->req_len >> 7) + 1; + if (cadence > SET_INTR_AT_REQ) + cadence = SET_INTR_AT_REQ; + if (pce_dev->intr_cadence < cadence || ((pce_dev->intr_cadence + == cadence) && pce_dev->cadence_flag)) + atomic_inc(&pce_dev->bunch_cmd_seq); + else { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); pce_dev->intr_cadence = 0; atomic_set(&pce_dev->bunch_cmd_seq, 0); atomic_set(&pce_dev->last_intr_seq, 0); - } else { - atomic_inc(&pce_dev->bunch_cmd_seq); + pce_dev->cadence_flag = ~pce_dev->cadence_flag; } } - spin_unlock_irqrestore(&pce_dev->lock, flags); return 0; } @@ -4746,6 +4753,7 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_AEAD; + preq_info->req_len = totallen_in; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -4804,8 +4812,9 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req) _qce_ccm_get_around_output(pce_dev, preq_info, q_req->dir); select_mode(pce_dev, preq_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); } - rc = _qce_sps_transfer(pce_dev, req_info); if (rc) goto bad; return 0; @@ -4973,6 +4982,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_AEAD; + preq_info->req_len = totallen; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5013,6 +5023,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req) SPS_IOVEC_FLAG_INT); pce_sps_data->producer_state = QCE_PIPE_STATE_COMP; } + rc = _qce_sps_transfer(pce_dev, req_info); } else { if (_qce_sps_add_sg_data(pce_dev, areq->src, totallen, &pce_sps_data->in_transfer)) @@ -5040,8 +5051,9 @@ int qce_aead_req(void *handle, struct qce_req *q_req) pce_sps_data->producer_state = QCE_PIPE_STATE_IDLE; } select_mode(pce_dev, preq_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); } - rc = _qce_sps_transfer(pce_dev, req_info); if (rc) goto bad; return 0; @@ -5129,6 +5141,7 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_CIPHERING; + preq_info->req_len = areq->nbytes; _qce_sps_iovec_count_init(pce_dev, req_info); if (pce_dev->support_cmd_dscr) @@ -5160,8 +5173,8 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) } select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; @@ -5233,6 +5246,7 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_HASHING; + preq_info->req_len = sreq->size; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5261,11 +5275,14 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq) &pce_sps_data->out_transfer)) goto bad; - if (is_dummy) + if (is_dummy) { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); - else + rc = _qce_sps_transfer(pce_dev, req_info); + } else { select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); + } if (rc) goto bad; return 0; @@ -5353,6 +5370,7 @@ int qce_f8_req(void *handle, struct qce_f8_req *req, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F8; + preq_info->req_len = req->data_len; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5378,8 +5396,8 @@ int qce_f8_req(void *handle, struct qce_f8_req *req, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; return 0; @@ -5468,6 +5486,7 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F8; + preq_info->req_len = total; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5492,8 +5511,8 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc == 0) return 0; @@ -5554,6 +5573,7 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F9; + preq_info->req_len = req->msize; _qce_sps_iovec_count_init(pce_dev, req_info); if (pce_dev->support_cmd_dscr) @@ -5573,8 +5593,8 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; return 0; @@ -5939,9 +5959,7 @@ void *qce_open(struct platform_device *pdev, int *rc) qce_setup_ce_sps_data(pce_dev); qce_disable_clk(pce_dev); setup_dummy_req(pce_dev); - spin_lock_init(&pce_dev->lock); - spin_lock_init(&pce_dev->sps_lock); - pce_dev->no_of_queued_req = 0; + atomic_set(&pce_dev->no_of_queued_req, 0); pce_dev->mode = IN_INTERRUPT_MODE; init_timer(&(pce_dev->timer)); pce_dev->timer.function = qce_multireq_timeout; @@ -5950,6 +5968,7 @@ void *qce_open(struct platform_device *pdev, int *rc) pce_dev->intr_cadence = 0; pce_dev->dev_no = pcedev_no; pcedev_no++; + pce_dev->owner = QCE_OWNER_NONE; mutex_unlock(&qce_iomap_mutex); return pce_dev; err: diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h index cef466382ee4..6dba3664ff08 100644 --- a/drivers/crypto/msm/qce50.h +++ b/drivers/crypto/msm/qce50.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -232,6 +232,7 @@ struct ce_request_info { dma_addr_t phy_ota_src; dma_addr_t phy_ota_dst; unsigned int ota_size; + unsigned int req_len; }; struct qce_driver_stats { diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index ab026d24e978..faeff0b55202 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -32,6 +32,7 @@ #include <linux/cache.h> #include <linux/platform_data/qcom_crypto_device.h> #include <linux/msm-bus.h> +#include <linux/hardirq.h> #include <linux/qcrypto.h> #include <crypto/ctr.h> @@ -51,7 +52,7 @@ #include "qce.h" #define DEBUG_MAX_FNAME 16 -#define DEBUG_MAX_RW_BUF 2048 +#define DEBUG_MAX_RW_BUF 4096 #define QCRYPTO_BIG_NUMBER 9999999 /* a big number */ /* @@ -131,6 +132,7 @@ struct qcrypto_req_control { struct crypto_engine *pce; struct crypto_async_request *req; struct qcrypto_resp_ctx *arsp; + int res; /* execution result */ }; struct crypto_engine { @@ -167,8 +169,14 @@ struct crypto_engine { unsigned int max_req; struct qcrypto_req_control *preq_pool; atomic_t req_count; + bool issue_req; /* an request is being issued to qce */ + bool first_engine; /* this engine is the first engine or not */ + unsigned int irq_cpu; /* the cpu running the irq of this engine */ + unsigned int max_req_used; /* debug stats */ }; +#define MAX_SMP_CPU 8 + struct crypto_priv { /* CE features supported by target device*/ struct msm_ce_hw_support platform_support; @@ -208,21 +216,37 @@ struct crypto_priv { enum resp_workq_sts sched_resp_workq_status; enum req_processing_sts ce_req_proc_sts; int cpu_getting_irqs_frm_first_ce; + struct crypto_engine *first_engine; + struct crypto_engine *scheduled_eng; /* last engine scheduled */ + + /* debug stats */ + unsigned no_avail; + unsigned resp_stop; + unsigned resp_start; + unsigned max_qlen; + unsigned int queue_work_eng3; + unsigned int queue_work_not_eng3; + unsigned int queue_work_not_eng3_nz; + unsigned int max_resp_qlen; + unsigned int max_reorder_cnt; + unsigned int cpu_req[MAX_SMP_CPU+1]; }; static struct crypto_priv qcrypto_dev; static struct crypto_engine *_qcrypto_static_assign_engine( struct crypto_priv *cp); static struct crypto_engine *_avail_eng(struct crypto_priv *cp); - static struct qcrypto_req_control *qcrypto_alloc_req_control( struct crypto_engine *pce) { int i; struct qcrypto_req_control *pqcrypto_req_control = pce->preq_pool; + unsigned int req_count; for (i = 0; i < pce->max_req; i++) { if (xchg(&pqcrypto_req_control->in_use, true) == false) { - atomic_inc(&pce->req_count); + req_count = atomic_inc_return(&pce->req_count); + if (req_count > pce->max_req_used) + pce->max_req_used = req_count; return pqcrypto_req_control; } pqcrypto_req_control++; @@ -233,11 +257,13 @@ static struct qcrypto_req_control *qcrypto_alloc_req_control( static void qcrypto_free_req_control(struct crypto_engine *pce, struct qcrypto_req_control *preq) { + /* do this before free req */ + preq->req = NULL; + preq->arsp = NULL; + /* free req */ if (xchg(&preq->in_use, false) == false) { pr_warn("request info %p free already\n", preq); } else { - preq->req = NULL; - preq->arsp = NULL; atomic_dec(&pce->req_count); } } @@ -441,7 +467,9 @@ struct qcrypto_cipher_req_ctx { #define SHA_MAX_DIGEST_SIZE SHA256_DIGEST_SIZE #define MSM_QCRYPTO_REQ_QUEUE_LENGTH 768 -#define COMPLETION_CB_BACKLOG_LENGTH 768 +#define COMPLETION_CB_BACKLOG_LENGTH_STOP 400 +#define COMPLETION_CB_BACKLOG_LENGTH_START \ + (COMPLETION_CB_BACKLOG_LENGTH_STOP / 2) static uint8_t _std_init_vector_sha1_uint8[] = { 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89, @@ -1066,6 +1094,7 @@ static int _disp_stats(int id) unsigned long flags; struct crypto_priv *cp = &qcrypto_dev; struct crypto_engine *pe; + int i; pstat = &_qcrypto_stat; len = scnprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1, @@ -1188,14 +1217,27 @@ static int _disp_stats(int id) " AHASH operation fail : %llu\n", pstat->ahash_op_fail); len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " resp start, resp stop, max rsp queue reorder-cnt : %u %u %u %u\n", + cp->resp_start, cp->resp_stop, + cp->max_resp_qlen, cp->max_reorder_cnt); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " max queue legnth, no avail : %u %u\n", + cp->max_qlen, cp->no_avail); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " work queue : %u %u %u\n", + cp->queue_work_eng3, + cp->queue_work_not_eng3, + cp->queue_work_not_eng3_nz); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, "\n"); spin_lock_irqsave(&cp->lock, flags); list_for_each_entry(pe, &cp->engine_list, elist) { len += scnprintf( _debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, - " Engine %4d Req : %llu\n", + " Engine %4d Req max %d : %llu\n", pe->unit, + pe->max_req_used, pe->total_req ); len += scnprintf( @@ -1208,6 +1250,14 @@ static int _disp_stats(int id) qce_get_driver_stats(pe->qce); } spin_unlock_irqrestore(&cp->lock, flags); + + for (i = 0; i < MAX_SMP_CPU+1; i++) + if (cp->cpu_req[i]) + len += scnprintf( + _debug_read_buf + len, + DEBUG_MAX_RW_BUF - len - 1, + "CPU %d Issue Req : %d\n", + i, cp->cpu_req[i]); return len; } @@ -1217,13 +1267,25 @@ static void _qcrypto_remove_engine(struct crypto_engine *pengine) struct qcrypto_alg *q_alg; struct qcrypto_alg *n; unsigned long flags; + struct crypto_engine *pe; cp = pengine->pcp; spin_lock_irqsave(&cp->lock, flags); list_del(&pengine->elist); + if (pengine->first_engine) { + cp->first_engine = NULL; + pe = list_first_entry(&cp->engine_list, struct crypto_engine, + elist); + if (pe) { + pe->first_engine = true; + cp->first_engine = pe; + } + } if (cp->next_engine == pengine) cp->next_engine = NULL; + if (cp->scheduled_eng == pengine) + cp->scheduled_eng = NULL; spin_unlock_irqrestore(&cp->lock, flags); cp->total_units--; @@ -1432,41 +1494,15 @@ static int _qcrypto_setkey_3des(struct crypto_ablkcipher *cipher, const u8 *key, return 0; }; -static struct crypto_engine *eng_sel_avoid_first(struct crypto_priv *cp) -{ - /* - * This function need not be spinlock protected when called from - * the seq_response workq as it will not have any contentions when all - * request processing is stopped. - */ - struct crypto_engine *p; - struct crypto_engine *q = NULL; - int max_user = QCRYPTO_BIG_NUMBER; - int use_cnt; - - if (unlikely(list_empty(&cp->engine_list))) { - pr_err("%s: no valid ce to schedule\n", __func__); - return NULL; - } - - p = list_first_entry(&cp->engine_list, struct crypto_engine, - elist); - list_for_each_entry_continue(p, &cp->engine_list, elist) { - use_cnt = atomic_read(&p->req_count); - if ((use_cnt < p->max_req) && (use_cnt < max_user)) { - q = p; - max_user = use_cnt; - } - } - return q; -} - static void seq_response(struct work_struct *work) { struct crypto_priv *cp = container_of(work, struct crypto_priv, resp_work); struct llist_node *list; struct llist_node *rev = NULL; + struct crypto_engine *pengine; + unsigned long flags; + int total_unit; again: list = llist_del_all(&cp->ordered_resp_list); @@ -1485,7 +1521,6 @@ again: while (rev) { struct qcrypto_resp_ctx *arsp; struct crypto_async_request *areq; - struct crypto_engine *pengine; arsp = container_of(rev, struct qcrypto_resp_ctx, llist); rev = llist_next(rev); @@ -1495,12 +1530,20 @@ again: areq->complete(areq, arsp->res); local_bh_enable(); atomic_dec(&cp->resp_cnt); - if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED && - atomic_read(&cp->resp_cnt) <= - (COMPLETION_CB_BACKLOG_LENGTH / 2)) { - pengine = eng_sel_avoid_first(cp); + } + + if (atomic_read(&cp->resp_cnt) < COMPLETION_CB_BACKLOG_LENGTH_START && + (cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS) + == STOPPED)) { + cp->resp_start++; + for (total_unit = cp->total_units; total_unit-- > 0;) { + spin_lock_irqsave(&cp->lock, flags); + pengine = _avail_eng(cp); + spin_unlock_irqrestore(&cp->lock, flags); if (pengine) _start_qcrypto_process(cp, pengine); + else + break; } } end: @@ -1512,12 +1555,19 @@ end: goto end; } -static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, - void *tfm_ctx) +#define SCHEUDLE_RSP_QLEN_THRESHOLD 64 + +static void _qcrypto_tfm_complete(struct crypto_engine *pengine, u32 type, + void *tfm_ctx, + struct qcrypto_resp_ctx *cur_arsp, + int res) { + struct crypto_priv *cp = pengine->pcp; unsigned long flags; struct qcrypto_resp_ctx *arsp; struct list_head *plist; + unsigned int resp_qlen; + unsigned int cnt = 0; switch (type) { case CRYPTO_ALG_TYPE_AHASH: @@ -1531,6 +1581,8 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, } spin_lock_irqsave(&cp->lock, flags); + + cur_arsp->res = res; while (!list_empty(plist)) { arsp = list_first_entry(plist, struct qcrypto_resp_ctx, list); @@ -1539,16 +1591,51 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, else { list_del(&arsp->list); llist_add(&arsp->llist, &cp->ordered_resp_list); + atomic_inc(&cp->resp_cnt); + cnt++; } } + resp_qlen = atomic_read(&cp->resp_cnt); + if (resp_qlen > cp->max_resp_qlen) + cp->max_resp_qlen = resp_qlen; + if (cnt > cp->max_reorder_cnt) + cp->max_reorder_cnt = cnt; + if ((resp_qlen >= COMPLETION_CB_BACKLOG_LENGTH_STOP) && + cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, + STOPPED) == IN_PROGRESS) { + cp->resp_stop++; + } + spin_unlock_irqrestore(&cp->lock, flags); retry: if (!llist_empty(&cp->ordered_resp_list)) { + unsigned int cpu; + + if (pengine->first_engine) { + cpu = WORK_CPU_UNBOUND; + cp->queue_work_eng3++; + } else { + cp->queue_work_not_eng3++; + cpu = cp->cpu_getting_irqs_frm_first_ce; + /* + * If source not the first engine, and there + * are outstanding requests going on first engine, + * skip scheduling of work queue to anticipate + * more may be coming. If the response queue + * length exceeds threshold, to avoid further + * delay, schedule work queue immediately. + */ + if (cp->first_engine && atomic_read( + &cp->first_engine->req_count)) { + if (resp_qlen < SCHEUDLE_RSP_QLEN_THRESHOLD) + return; + cp->queue_work_not_eng3_nz++; + } + } if (cmpxchg(&cp->sched_resp_workq_status, NOT_SCHEDULED, IS_SCHEDULED) == NOT_SCHEDULED) - queue_work_on(cp->cpu_getting_irqs_frm_first_ce, - cp->resp_wq, &cp->resp_work); + queue_work_on(cpu, cp->resp_wq, &cp->resp_work); else if (cmpxchg(&cp->sched_resp_workq_status, IS_SCHEDULED, SCHEDULE_AGAIN) == NOT_SCHEDULED) goto retry; @@ -1559,36 +1646,34 @@ static void req_done(struct qcrypto_req_control *pqcrypto_req_control) { struct crypto_engine *pengine; struct crypto_async_request *areq; - struct crypto_engine *pe; struct crypto_priv *cp; - unsigned long flags; struct qcrypto_resp_ctx *arsp; u32 type = 0; void *tfm_ctx = NULL; + unsigned int cpu; + int res; pengine = pqcrypto_req_control->pce; cp = pengine->pcp; - spin_lock_irqsave(&cp->lock, flags); areq = pqcrypto_req_control->req; arsp = pqcrypto_req_control->arsp; + res = pqcrypto_req_control->res; qcrypto_free_req_control(pengine, pqcrypto_req_control); if (areq) { type = crypto_tfm_alg_type(areq->tfm); tfm_ctx = crypto_tfm_ctx(areq->tfm); } - pe = list_first_entry(&cp->engine_list, struct crypto_engine, elist); - if (pe == pengine) - if (cp->cpu_getting_irqs_frm_first_ce != smp_processor_id()) - cp->cpu_getting_irqs_frm_first_ce = smp_processor_id(); - spin_unlock_irqrestore(&cp->lock, flags); - if (atomic_read(&cp->resp_cnt) <= COMPLETION_CB_BACKLOG_LENGTH) { - cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS); - _start_qcrypto_process(cp, pengine); - } else - cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED); + cpu = smp_processor_id(); + pengine->irq_cpu = cpu; + if (pengine->first_engine) { + if (cpu != cp->cpu_getting_irqs_frm_first_ce) + cp->cpu_getting_irqs_frm_first_ce = cpu; + } if (areq) - _qcrypto_tfm_complete(cp, type, tfm_ctx); + _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, res); + if (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS) + _start_qcrypto_process(cp, pengine); } static void _qce_ahash_complete(void *cookie, unsigned char *digest, @@ -1639,10 +1724,10 @@ static void _qce_ahash_complete(void *cookie, unsigned char *digest, rctx->first_blk = 0; if (ret) { - pqcrypto_req_control->arsp->res = -ENXIO; + pqcrypto_req_control->res = -ENXIO; pstat->ahash_op_fail++; } else { - pqcrypto_req_control->arsp->res = 0; + pqcrypto_req_control->res = 0; pstat->ahash_op_success++; } if (cp->ce_support.aligned_only) { @@ -1684,10 +1769,10 @@ static void _qce_ablk_cipher_complete(void *cookie, unsigned char *icb, memcpy(ctx->iv, iv, crypto_ablkcipher_ivsize(ablk)); if (ret) { - pqcrypto_req_control->arsp->res = -ENXIO; + pqcrypto_req_control->res = -ENXIO; pstat->ablk_cipher_op_fail++; } else { - pqcrypto_req_control->arsp->res = 0; + pqcrypto_req_control->res = 0; pstat->ablk_cipher_op_success++; } @@ -1773,7 +1858,7 @@ static void _qce_aead_complete(void *cookie, unsigned char *icv, else pstat->aead_op_success++; - pqcrypto_req_control->arsp->res = ret; + pqcrypto_req_control->res = ret; req_done(pqcrypto_req_control); } @@ -2100,12 +2185,24 @@ static int _start_qcrypto_process(struct crypto_priv *cp, struct aead_request *aead_req; struct qcrypto_resp_ctx *arsp; struct qcrypto_req_control *pqcrypto_req_control; + unsigned int cpu = MAX_SMP_CPU; + + if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED) + return 0; + + if (in_interrupt()) { + cpu = smp_processor_id(); + if (cpu >= MAX_SMP_CPU) + cpu = MAX_SMP_CPU - 1; + } else + cpu = MAX_SMP_CPU; pstat = &_qcrypto_stat; again: spin_lock_irqsave(&cp->lock, flags); - if (atomic_read(&pengine->req_count) >= (pengine->max_req)) { + if (pengine->issue_req || + atomic_read(&pengine->req_count) >= (pengine->max_req)) { spin_unlock_irqrestore(&cp->lock, flags); return 0; } @@ -2176,7 +2273,6 @@ again: break; } - atomic_inc(&cp->resp_cnt); arsp->res = -EINPROGRESS; arsp->async_req = async_req; pqcrypto_req_control->pce = pengine; @@ -2185,6 +2281,10 @@ again: pengine->active_seq++; pengine->check_flag = true; + pengine->issue_req = true; + cp->cpu_req[cpu]++; + smp_mb(); /* make it visible */ + spin_unlock_irqrestore(&cp->lock, flags); if (backlog_eng) backlog_eng->complete(backlog_eng, -EINPROGRESS); @@ -2204,9 +2304,12 @@ again: default: ret = -EINVAL; }; + + pengine->issue_req = false; + smp_mb(); /* make it visible */ + pengine->total_req++; if (ret) { - arsp->res = ret; pengine->err_req++; qcrypto_free_req_control(pengine, pqcrypto_req_control); @@ -2218,32 +2321,48 @@ again: else pstat->aead_op_fail++; - _qcrypto_tfm_complete(cp, type, tfm_ctx); + _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, ret); goto again; }; return ret; } +static inline struct crypto_engine *_next_eng(struct crypto_priv *cp, + struct crypto_engine *p) +{ + + if (p == NULL || list_is_last(&p->elist, &cp->engine_list)) + p = list_first_entry(&cp->engine_list, struct crypto_engine, + elist); + else + p = list_entry(p->elist.next, struct crypto_engine, elist); + return p; +} static struct crypto_engine *_avail_eng(struct crypto_priv *cp) { /* call this function with spinlock set */ - struct crypto_engine *p; struct crypto_engine *q = NULL; - int max_user = QCRYPTO_BIG_NUMBER; - int use_cnt; + struct crypto_engine *p = cp->scheduled_eng; + struct crypto_engine *q1; + int eng_cnt = cp->total_units; if (unlikely(list_empty(&cp->engine_list))) { pr_err("%s: no valid ce to schedule\n", __func__); return NULL; } - list_for_each_entry(p, &cp->engine_list, elist) { - use_cnt = atomic_read(&p->req_count); - if ((use_cnt < p->max_req) && (use_cnt < max_user)) { + p = _next_eng(cp, p); + q1 = p; + while (eng_cnt-- > 0) { + if (!p->issue_req && atomic_read(&p->req_count) < p->max_req) { q = p; - max_user = use_cnt; + break; } + p = _next_eng(cp, p); + if (q1 == p) + break; } + cp->scheduled_eng = q; return q; } @@ -2261,6 +2380,8 @@ static int _qcrypto_queue_req(struct crypto_priv *cp, } else { ret = crypto_enqueue_request(&cp->req_queue, req); pengine = _avail_eng(cp); + if (cp->req_queue.qlen > cp->max_qlen) + cp->max_qlen = cp->req_queue.qlen; } if (pengine) { switch (pengine->bw_state) { @@ -2286,16 +2407,12 @@ static int _qcrypto_queue_req(struct crypto_priv *cp, pengine = NULL; break; } + } else { + cp->no_avail++; } spin_unlock_irqrestore(&cp->lock, flags); - if (pengine) { - if (atomic_read(&cp->resp_cnt) <= - COMPLETION_CB_BACKLOG_LENGTH) { - cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS); - _start_qcrypto_process(cp, pengine); - } else - cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED); - } + if (pengine && (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS)) + _start_qcrypto_process(cp, pengine); return ret; } @@ -4762,6 +4879,8 @@ static int _qcrypto_probe(struct platform_device *pdev) pengine->active_seq = 0; pengine->last_active_seq = 0; pengine->check_flag = false; + pengine->max_req_used = 0; + pengine->issue_req = false; crypto_init_queue(&pengine->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH); @@ -4770,6 +4889,9 @@ static int _qcrypto_probe(struct platform_device *pdev) pengine->unit = cp->total_units; spin_lock_irqsave(&cp->lock, flags); + pengine->first_engine = list_empty(&cp->engine_list); + if (pengine->first_engine) + cp->first_engine = pengine; list_add_tail(&pengine->elist, &cp->engine_list); cp->next_engine = pengine; spin_unlock_irqrestore(&cp->lock, flags); @@ -5292,6 +5414,7 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf, unsigned long flags; struct crypto_priv *cp = &qcrypto_dev; struct crypto_engine *pe; + int i; memset((char *)&_qcrypto_stat, 0, sizeof(struct crypto_stat)); spin_lock_irqsave(&cp->lock, flags); @@ -5299,7 +5422,19 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf, pe->total_req = 0; pe->err_req = 0; qce_clear_driver_stats(pe->qce); + pe->max_req_used = 0; } + cp->max_qlen = 0; + cp->resp_start = 0; + cp->resp_stop = 0; + cp->no_avail = 0; + cp->max_resp_qlen = 0; + cp->queue_work_eng3 = 0; + cp->queue_work_not_eng3 = 0; + cp->queue_work_not_eng3_nz = 0; + cp->max_reorder_cnt = 0; + for (i = 0; i < MAX_SMP_CPU + 1; i++) + cp->cpu_req[i] = 0; spin_unlock_irqrestore(&cp->lock, flags); return count; } @@ -5362,6 +5497,8 @@ static int __init _qcrypto_init(void) pcp->total_units = 0; pcp->platform_support.bus_scale_table = NULL; pcp->next_engine = NULL; + pcp->scheduled_eng = NULL; + pcp->ce_req_proc_sts = IN_PROGRESS; crypto_init_queue(&pcp->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH); return platform_driver_register(&_qualcomm_crypto); } diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 918231b73215..3052166c7a18 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1760,6 +1760,30 @@ static int adreno_getproperty(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QDSS_STM: + { + struct kgsl_qdss_stm_prop qdssprop = {0}; + struct kgsl_memdesc *qdss_desc = + kgsl_mmu_get_qdss_global_entry(device); + + if (sizebytes != sizeof(qdssprop)) { + status = -EINVAL; + break; + } + + if (qdss_desc) { + qdssprop.gpuaddr = qdss_desc->gpuaddr; + qdssprop.size = qdss_desc->size; + } + + if (copy_to_user(value, &qdssprop, + sizeof(qdssprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; case KGSL_PROP_MMU_ENABLE: { /* Report MMU only if we can handle paged memory */ diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c index 582cbfb61e78..d86a0c60f0b4 100644 --- a/drivers/gpu/msm/adreno_compat.c +++ b/drivers/gpu/msm/adreno_compat.c @@ -89,6 +89,30 @@ int adreno_getproperty_compat(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QDSS_STM: + { + struct kgsl_qdss_stm_prop qdssprop = {0}; + struct kgsl_memdesc *qdss_desc = + kgsl_mmu_get_qdss_global_entry(device); + + if (sizebytes != sizeof(qdssprop)) { + status = -EINVAL; + break; + } + + if (qdss_desc) { + qdssprop.gpuaddr = qdss_desc->gpuaddr; + qdssprop.size = qdss_desc->size; + } + + if (copy_to_user(value, &qdssprop, + sizeof(qdssprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; default: /* * Call the adreno_getproperty to check if the property type diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 103d290eb681..865cd9d8f498 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -96,6 +96,7 @@ static struct kgsl_memdesc *global_pt_entries[GLOBAL_PT_ENTRIES]; static struct kgsl_memdesc *kgsl_global_secure_pt_entry; static int global_pt_count; uint64_t global_pt_alloc; +static struct kgsl_memdesc gpu_qdss_desc; static void kgsl_iommu_unmap_globals(struct kgsl_pagetable *pagetable) { @@ -183,6 +184,51 @@ void kgsl_add_global_secure_entry(struct kgsl_device *device, kgsl_global_secure_pt_entry = memdesc; } +struct kgsl_memdesc *kgsl_iommu_get_qdss_global_entry(void) +{ + return &gpu_qdss_desc; +} + +static void kgsl_setup_qdss_desc(struct kgsl_device *device) +{ + int result = 0; + uint32_t gpu_qdss_entry[2]; + + if (!of_find_property(device->pdev->dev.of_node, + "qcom,gpu-qdss-stm", NULL)) + return; + + if (of_property_read_u32_array(device->pdev->dev.of_node, + "qcom,gpu-qdss-stm", gpu_qdss_entry, 2)) { + KGSL_CORE_ERR("Failed to read gpu qdss dts entry\n"); + return; + } + + gpu_qdss_desc.flags = 0; + gpu_qdss_desc.priv = 0; + gpu_qdss_desc.physaddr = gpu_qdss_entry[0]; + gpu_qdss_desc.size = gpu_qdss_entry[1]; + gpu_qdss_desc.pagetable = NULL; + gpu_qdss_desc.ops = NULL; + gpu_qdss_desc.dev = device->dev->parent; + gpu_qdss_desc.hostptr = NULL; + + result = memdesc_sg_dma(&gpu_qdss_desc, gpu_qdss_desc.physaddr, + gpu_qdss_desc.size); + if (result) { + KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result); + return; + } + + kgsl_mmu_add_global(device, &gpu_qdss_desc); +} + +static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu) +{ + kgsl_iommu_remove_global(mmu, &gpu_qdss_desc); + kgsl_sharedmem_free(&gpu_qdss_desc); +} + static inline void _iommu_sync_mmu_pc(bool lock) { @@ -1265,6 +1311,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu) kgsl_iommu_remove_global(mmu, &iommu->setstate); kgsl_sharedmem_free(&iommu->setstate); + kgsl_cleanup_qdss_desc(mmu); } static int _setstate_alloc(struct kgsl_device *device, @@ -1336,6 +1383,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) } kgsl_iommu_add_global(mmu, &iommu->setstate); + kgsl_setup_qdss_desc(device); done: if (status) @@ -2341,6 +2389,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = { .mmu_add_global = kgsl_iommu_add_global, .mmu_remove_global = kgsl_iommu_remove_global, .mmu_getpagetable = kgsl_iommu_getpagetable, + .mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry, .probe = kgsl_iommu_probe, }; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index f8315090ff06..8b0d93fda32c 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -546,6 +546,17 @@ bool kgsl_mmu_gpuaddr_in_range(struct kgsl_pagetable *pagetable, } EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range); +struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + + if (MMU_OP_VALID(mmu, mmu_get_qdss_global_entry)) + return mmu->mmu_ops->mmu_get_qdss_global_entry(); + + return NULL; +} +EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry); + /* * NOMMU defintions - NOMMU really just means that the MMU is kept in pass * through and the GPU directly accesses physical memory. Used in debug mode and diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 5339917911b1..588777af353f 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -80,6 +80,7 @@ struct kgsl_mmu_ops { struct kgsl_memdesc *memdesc); struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu, unsigned long name); + struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void); }; struct kgsl_mmu_pt_ops { @@ -227,6 +228,8 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset, uint64_t size); +struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device); + /* * Static inline functions of MMU that simply call the SMMU specific * function using a function pointer. These functions can be thought diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c index ba54e7bde795..3776e748647e 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -38,6 +38,7 @@ #define PMI_CHG_SCALE_1 -138890 #define PMI_CHG_SCALE_2 391750000000 #define QPNP_VADC_HC_VREF_CODE 0x4000 +#define QPNP_VADC_HC_VDD_REFERENCE_MV 1875 /* Units for temperature below (on x axis) is in 0.1DegC as required by the battery driver. Note the resolution used @@ -789,41 +790,51 @@ int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0, sign = 0; - rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_ABSOLUTE); - if (rc < 0) { - pr_err("Could not acquire gain and offset\n"); - return rc; - } - /* Convert to Kelvin and account for voltage to be written as 2mV/K */ low_output = (param->low_temp + KELVINMIL_DEGMIL) * 2; - /* Convert to voltage threshold */ - low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy; - if (low_output < 0) { - sign = 1; - low_output = -low_output; - } - do_div(low_output, QPNP_ADC_625_UV); - if (sign) - low_output = -low_output; - low_output += btm_param.adc_gnd; - - sign = 0; /* Convert to Kelvin and account for voltage to be written as 2mV/K */ high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2; - /* Convert to voltage threshold */ - high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy; - if (high_output < 0) { - sign = 1; - high_output = -high_output; + + if (param->adc_tm_hc) { + low_output *= QPNP_VADC_HC_VREF_CODE; + do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + high_output *= QPNP_VADC_HC_VREF_CODE; + do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + } else { + rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param, + CALIB_ABSOLUTE); + if (rc < 0) { + pr_err("Could not acquire gain and offset\n"); + return rc; + } + + /* Convert to voltage threshold */ + low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy; + if (low_output < 0) { + sign = 1; + low_output = -low_output; + } + do_div(low_output, QPNP_ADC_625_UV); + if (sign) + low_output = -low_output; + low_output += btm_param.adc_gnd; + + sign = 0; + /* Convert to voltage threshold */ + high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy; + if (high_output < 0) { + sign = 1; + high_output = -high_output; + } + do_div(high_output, QPNP_ADC_625_UV); + if (sign) + high_output = -high_output; + high_output += btm_param.adc_gnd; } - do_div(high_output, QPNP_ADC_625_UV); - if (sign) - high_output = -high_output; - high_output += btm_param.adc_gnd; *low_threshold = (uint32_t) low_output; *high_threshold = (uint32_t) high_output; + pr_debug("high_temp:%d, low_temp:%d\n", param->high_temp, param->low_temp); pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold, @@ -1079,29 +1090,34 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip, { int64_t adc_voltage = 0; struct qpnp_vadc_linear_graph param1; - int negative_offset; - - qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + int negative_offset = 0; - adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref; - if (adc_voltage < 0) { - negative_offset = 1; - adc_voltage = -adc_voltage; - } - - do_div(adc_voltage, param1.dy); - - if (adc_properties->adc_hc) + if (adc_properties->adc_hc) { + /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + adc_voltage = (int64_t) reg; + adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV; + adc_voltage = div64_s64(adc_voltage, + QPNP_VADC_HC_VREF_CODE); qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), adc_voltage, result); - else + } else { + qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + + adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref; + if (adc_voltage < 0) { + negative_offset = 1; + adc_voltage = -adc_voltage; + } + + do_div(adc_voltage, param1.dy); + qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), adc_voltage, result); - - if (negative_offset) - adc_voltage = -adc_voltage; + if (negative_offset) + adc_voltage = -adc_voltage; + } return 0; } @@ -1114,8 +1130,6 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip, struct qpnp_vadc_linear_graph param1; int rc; - qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); - if (adc_properties->adc_hc) { rc = qpnp_adc_map_temp_voltage( adcmap_100k_104ef_104fb_1875_vref, @@ -1123,27 +1137,40 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip, param->low_thr_temp, ¶m->low_thr_voltage); if (rc) return rc; + param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); + + rc = qpnp_adc_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + return rc; + param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); } else { + qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), param->low_thr_temp, ¶m->low_thr_voltage); if (rc) return rc; - } - param->low_thr_voltage *= param1.dy; - do_div(param->low_thr_voltage, param1.adc_vref); - param->low_thr_voltage += param1.adc_gnd; + param->low_thr_voltage *= param1.dy; + do_div(param->low_thr_voltage, param1.adc_vref); + param->low_thr_voltage += param1.adc_gnd; - rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, - ARRAY_SIZE(adcmap_100k_104ef_104fb), - param->high_thr_temp, ¶m->high_thr_voltage); - if (rc) - return rc; + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, + ARRAY_SIZE(adcmap_100k_104ef_104fb), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + return rc; - param->high_thr_voltage *= param1.dy; - do_div(param->high_thr_voltage, param1.adc_vref); - param->high_thr_voltage += param1.adc_gnd; + param->high_thr_voltage *= param1.dy; + do_div(param->high_thr_voltage, param1.adc_vref); + param->high_thr_voltage += param1.adc_gnd; + } return 0; } @@ -1251,7 +1278,7 @@ int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *chip, } EXPORT_SYMBOL(qpnp_adc_usb_scaler); -int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, +int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param, uint32_t *low_threshold, uint32_t *high_threshold) { @@ -1259,32 +1286,49 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, int rc = 0, sign = 0; int64_t low_thr = 0, high_thr = 0; - rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE); - if (rc < 0) - return rc; - - low_thr = (((param->low_thr/param->gain_den) - QPNP_ADC_625_UV) * - vbatt_param.dy); - if (low_thr < 0) { - sign = 1; - low_thr = -low_thr; - } - do_div(low_thr, QPNP_ADC_625_UV); - if (sign) - low_thr = -low_thr; - *low_threshold = low_thr + vbatt_param.adc_gnd; + if (param->adc_tm_hc) { + low_thr = (param->low_thr/param->gain_den); + low_thr *= param->gain_num; + low_thr *= QPNP_VADC_HC_VREF_CODE; + do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + *low_threshold = low_thr; + + high_thr = (param->high_thr/param->gain_den); + high_thr *= param->gain_num; + high_thr *= QPNP_VADC_HC_VREF_CODE; + do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + *high_threshold = high_thr; + } else { + rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, + CALIB_ABSOLUTE); + if (rc < 0) + return rc; - sign = 0; - high_thr = (((param->high_thr/param->gain_den) - QPNP_ADC_625_UV) * - vbatt_param.dy); - if (high_thr < 0) { - sign = 1; - high_thr = -high_thr; + low_thr = (((param->low_thr/param->gain_den) - + QPNP_ADC_625_UV) * vbatt_param.dy); + if (low_thr < 0) { + sign = 1; + low_thr = -low_thr; + } + low_thr = low_thr * param->gain_num; + do_div(low_thr, QPNP_ADC_625_UV); + if (sign) + low_thr = -low_thr; + *low_threshold = low_thr + vbatt_param.adc_gnd; + + sign = 0; + high_thr = (((param->high_thr/param->gain_den) - + QPNP_ADC_625_UV) * vbatt_param.dy); + if (high_thr < 0) { + sign = 1; + high_thr = -high_thr; + } + high_thr = high_thr * param->gain_num; + do_div(high_thr, QPNP_ADC_625_UV); + if (sign) + high_thr = -high_thr; + *high_threshold = high_thr + vbatt_param.adc_gnd; } - do_div(high_thr, QPNP_ADC_625_UV); - if (sign) - high_thr = -high_thr; - *high_threshold = high_thr + vbatt_param.adc_gnd; pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr, param->low_thr); @@ -1292,48 +1336,16 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, *low_threshold); return 0; } -EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler); +EXPORT_SYMBOL(qpnp_adc_absolute_rthr); -int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip, +int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param, uint32_t *low_threshold, uint32_t *high_threshold) { - struct qpnp_vadc_linear_graph vbatt_param; - int rc = 0, sign = 0; - int64_t low_thr = 0, high_thr = 0; - - rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE); - if (rc < 0) - return rc; - - low_thr = (((param->low_thr) - QPNP_ADC_625_UV) * vbatt_param.dy); - if (low_thr < 0) { - sign = 1; - low_thr = -low_thr; - } - do_div(low_thr, QPNP_ADC_625_UV); - if (sign) - low_thr = -low_thr; - *low_threshold = low_thr + vbatt_param.adc_gnd; - - sign = 0; - high_thr = (((param->high_thr) - QPNP_ADC_625_UV) * vbatt_param.dy); - if (high_thr < 0) { - sign = 1; - high_thr = -high_thr; - } - do_div(high_thr, QPNP_ADC_625_UV); - if (sign) - high_thr = -high_thr; - *high_threshold = high_thr + vbatt_param.adc_gnd; - - pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr, - param->low_thr); - pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold, - *low_threshold); - return 0; + return qpnp_adc_absolute_rthr(chip, param, low_threshold, + high_threshold); } -EXPORT_SYMBOL(qpnp_adc_absolute_rthr); +EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler); int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *chip, const struct qpnp_vadc_chan_properties *chan_prop, @@ -1393,6 +1405,11 @@ int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1446,6 +1463,11 @@ int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1499,6 +1521,11 @@ int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1552,6 +1579,11 @@ int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c index f36bb933a03e..6ed947e5603b 100644 --- a/drivers/hwmon/qpnp-adc-voltage.c +++ b/drivers/hwmon/qpnp-adc-voltage.c @@ -1642,8 +1642,12 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc) vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy = (calib_read_1 - calib_read_2); - vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx + if (calib_type == CALIB_ABSOLUTE) + vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx = QPNP_ADC_625_UV; + else if (calib_type == ADC_HC_ABS_CAL) + vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx + = QPNP_ADC_1P25_UV; vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref = calib_read_1; vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd = diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 7e93f7654347..617c766f032e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -68,24 +68,8 @@ static bool etm4_arch_supported(u8 arch) static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - unsigned long flags; - int trace_id = -1; - if (!drvdata->enable) - return drvdata->trcid; - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR); - trace_id &= ETM_TRACEID_MASK; - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return trace_id; + return drvdata->trcid; } static void etm4_enable_hw(void *info) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index e0a50e814d44..10e50df1e6d5 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1837,7 +1837,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->size = SZ_1M; drvdata->mem_size = drvdata->size; - drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG; + + if (of_property_read_bool(np, "arm,sg-enable")) + drvdata->memtype = TMC_ETR_MEM_TYPE_SG; + else + drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG; drvdata->mem_type = drvdata->memtype; } else { drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 93ad9df1f294..529edb16565a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1153,4 +1153,17 @@ config TOUCHSCREEN_GEN_VKEYS To compile this driver as a module, choose M here: the module will be called gen_vkeys. +config TOUCHSCREEN_FT5X06 + tristate "FocalTech touchscreens" + depends on I2C + help + Say Y here if you have a ft5X06 touchscreen. + Ft5x06 controllers are multi touch controllers which can + report 5 touches at a time. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ft5x06_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index aaf7f587ed19..e04e787cea6e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o +obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c new file mode 100644 index 000000000000..c9905a4a87df --- /dev/null +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -0,0 +1,654 @@ +/* + * + * FocalTech ft5x06 TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/input/ft5x06_ts.h> + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +/* Early-suspend level */ +#define FT5X06_SUSPEND_LEVEL 1 +#endif + +#define CFG_MAX_TOUCH_POINTS 5 + +#define FT_STARTUP_DLY 150 +#define FT_RESET_DLY 20 + +#define FT_PRESS 0x7F +#define FT_MAX_ID 0x0F +#define FT_TOUCH_STEP 6 +#define FT_TOUCH_X_H_POS 3 +#define FT_TOUCH_X_L_POS 4 +#define FT_TOUCH_Y_H_POS 5 +#define FT_TOUCH_Y_L_POS 6 +#define FT_TOUCH_EVENT_POS 3 +#define FT_TOUCH_ID_POS 5 + +#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS) + +/*register address*/ +#define FT5X06_REG_PMODE 0xA5 +#define FT5X06_REG_FW_VER 0xA6 +#define FT5X06_REG_POINT_RATE 0x88 +#define FT5X06_REG_THGROUP 0x80 + +/* power register bits*/ +#define FT5X06_PMODE_ACTIVE 0x00 +#define FT5X06_PMODE_MONITOR 0x01 +#define FT5X06_PMODE_STANDBY 0x02 +#define FT5X06_PMODE_HIBERNATE 0x03 + +#define FT5X06_VTG_MIN_UV 2600000 +#define FT5X06_VTG_MAX_UV 3300000 +#define FT5X06_I2C_VTG_MIN_UV 1800000 +#define FT5X06_I2C_VTG_MAX_UV 1800000 + +struct ts_event { + u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */ + u16 y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */ + /* touch event: 0 -- down; 1-- contact; 2 -- contact */ + u8 touch_event[CFG_MAX_TOUCH_POINTS]; + u8 finger_id[CFG_MAX_TOUCH_POINTS]; /*touch ID */ + u16 pressure; + u8 touch_point; +}; + +struct ft5x06_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct ts_event event; + const struct ft5x06_ts_platform_data *pdata; + struct regulator *vdd; + struct regulator *vcc_i2c; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf, + int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static void ft5x06_report_value(struct ft5x06_ts_data *data) +{ + struct ts_event *event = &data->event; + int i; + int fingerdown = 0; + + for (i = 0; i < event->touch_point; i++) { + if (event->touch_event[i] == 0 || event->touch_event[i] == 2) { + event->pressure = FT_PRESS; + fingerdown++; + } else { + event->pressure = 0; + } + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, + event->x[i]); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, + event->y[i]); + input_report_abs(data->input_dev, ABS_MT_PRESSURE, + event->pressure); + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, + event->finger_id[i]); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, + event->pressure); + input_mt_sync(data->input_dev); + } + + input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); + input_sync(data->input_dev); +} + +static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) +{ + struct ts_event *event = &data->event; + int ret, i; + u8 buf[POINT_READ_BUF] = { 0 }; + u8 pointid = FT_MAX_ID; + + ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF); + if (ret < 0) { + dev_err(&data->client->dev, "%s read touchdata failed.\n", + __func__); + return ret; + } + memset(event, 0, sizeof(struct ts_event)); + + event->touch_point = 0; + for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { + pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; + if (pointid >= FT_MAX_ID) + break; + else + event->touch_point++; + event->x[i] = + (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) << + 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i]; + event->y[i] = + (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) << + 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i]; + event->touch_event[i] = + buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6; + event->finger_id[i] = + (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; + } + + ft5x06_report_value(data); + + return 0; +} + +static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) +{ + struct ft5x06_ts_data *data = dev_id; + int rc; + + rc = ft5x06_handle_touchdata(data); + if (rc) + pr_err("%s: handling touchdata failed\n", __func__); + + return IRQ_HANDLED; +} + +static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto power_off; + + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(data->vdd); + } + + return rc; + +power_off: + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c disable failed rc=%d\n", rc); + regulator_enable(data->vdd); + } + + return rc; +} + +static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV, + FT5X06_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV, + FT5X06_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); +reg_vdd_set_vtg: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + return rc; + +pwr_deinit: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV); + + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + return 0; +} + +#ifdef CONFIG_PM +static int ft5x06_ts_suspend(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + char txbuf[2]; + + disable_irq(data->client->irq); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + txbuf[0] = FT5X06_REG_PMODE; + txbuf[1] = FT5X06_PMODE_HIBERNATE; + ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); + } + + return 0; +} + +static int ft5x06_ts_resume(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(FT_RESET_DLY); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + enable_irq(data->client->irq); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ft5x06_ts_early_suspend(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + ft5x06_ts_suspend(&data->client->dev); +} + +static void ft5x06_ts_late_resume(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + ft5x06_ts_resume(&data->client->dev); +} +#endif + +static const struct dev_pm_ops ft5x06_ts_pm_ops = { +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = ft5x06_ts_suspend, + .resume = ft5x06_ts_resume, +#endif +}; +#endif + +static int ft5x06_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data; + struct ft5x06_ts_data *data; + struct input_dev *input_dev; + u8 reg_value; + u8 reg_addr; + int err; + + if (!pdata) { + dev_err(&client->dev, "Invalid pdata\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Not enough memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + dev_err(&client->dev, "failed to allocate input device\n"); + goto free_mem; + } + + data->input_dev = input_dev; + data->client = client; + data->pdata = pdata; + + input_dev->name = "ft5x06_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_set_drvdata(input_dev, data); + i2c_set_clientdata(client, data); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_MAX_TOUCH_POINTS, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, "Input device registration failed\n"); + goto free_inputdev; + } + + if (pdata->power_init) { + err = pdata->power_init(true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } else { + err = ft5x06_power_init(data, true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } + + if (pdata->power_on) { + err = pdata->power_on(true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } + + if (gpio_is_valid(pdata->irq_gpio)) { + err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio"); + if (err) { + dev_err(&client->dev, "irq gpio request failed"); + goto pwr_off; + } + err = gpio_direction_input(pdata->irq_gpio); + if (err) { + dev_err(&client->dev, + "set_direction for irq gpio failed\n"); + goto free_irq_gpio; + } + } + + if (gpio_is_valid(pdata->reset_gpio)) { + err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio"); + if (err) { + dev_err(&client->dev, "reset gpio request failed"); + goto free_irq_gpio; + } + + err = gpio_direction_output(pdata->reset_gpio, 0); + if (err) { + dev_err(&client->dev, + "set_direction for reset gpio failed\n"); + goto free_reset_gpio; + } + msleep(FT_RESET_DLY); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + /* make sure CTP already finish startup process */ + msleep(FT_STARTUP_DLY); + + /*get some register information */ + reg_addr = FT5X06_REG_FW_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "version read failed"); + + dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value); + + reg_addr = FT5X06_REG_POINT_RATE; + ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "report rate read failed"); + dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10); + + reg_addr = FT5X06_REG_THGROUP; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "threshold read failed"); + dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4); + + err = request_threaded_irq(client->irq, NULL, + ft5x06_ts_interrupt, pdata->irqflags, + client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "request irq failed\n"); + goto free_reset_gpio; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + FT5X06_SUSPEND_LEVEL; + data->early_suspend.suspend = ft5x06_ts_early_suspend; + data->early_suspend.resume = ft5x06_ts_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + return 0; + +free_reset_gpio: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); +free_irq_gpio: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->reset_gpio); +pwr_off: + if (pdata->power_on) + pdata->power_on(false); + else + ft5x06_power_on(data, false); +pwr_deinit: + if (pdata->power_init) + pdata->power_init(false); + else + ft5x06_power_init(data, false); +unreg_inputdev: + input_unregister_device(input_dev); + input_dev = NULL; +free_inputdev: + input_free_device(input_dev); +free_mem: + kfree(data); + return err; +} + +static int __devexit ft5x06_ts_remove(struct i2c_client *client) +{ + struct ft5x06_ts_data *data = i2c_get_clientdata(client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + free_irq(client->irq, data); + + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->reset_gpio); + + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft5x06_power_on(data, false); + + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft5x06_power_init(data, false); + + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ft5x06_ts_id[] = { + {"ft5x06_ts", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id); + +static struct i2c_driver ft5x06_ts_driver = { + .probe = ft5x06_ts_probe, + .remove = __devexit_p(ft5x06_ts_remove), + .driver = { + .name = "ft5x06_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ft5x06_ts_pm_ops, +#endif + }, + .id_table = ft5x06_ts_id, +}; + +static int __init ft5x06_ts_init(void) +{ + return i2c_add_driver(&ft5x06_ts_driver); +} +module_init(ft5x06_ts_init); + +static void __exit ft5x06_ts_exit(void) +{ + i2c_del_driver(&ft5x06_ts_driver); +} +module_exit(ft5x06_ts_exit); + +MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 048358e2ef9d..773ece9eb1d4 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -1,6 +1,7 @@ /* drivers/input/touchscreen/it7258_ts_i2c.c * * Copyright (C) 2014 ITE Tech. Inc. + * Copyright (c) 2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,31 +29,50 @@ #define SCREEN_X_RESOLUTION 320 #define SCREEN_Y_RESOLUTION 320 -#define BUF_COMMAND 0x20 /* all commands writes go to this idx */ +/* all commands writes go to this idx */ +#define BUF_COMMAND 0x20 #define BUF_SYS_COMMAND 0x40 -#define BUF_QUERY 0x80 /* "revice ready?" and "wake up please" and "read touch data" reads go to this idx */ -#define BUF_RESPONSE 0xA0 /* most command responce reads go to this idx */ +/* + * "device ready?" and "wake up please" and "read touch data" reads + * go to this idx + */ +#define BUF_QUERY 0x80 +/* most command response reads go to this idx */ +#define BUF_RESPONSE 0xA0 #define BUF_SYS_RESPONSE 0xC0 -#define BUF_POINT_INFO 0xE0 /* reads of "point" go through here and produce 14 bytes of data */ +/* reads of "point" go through here and produce 14 bytes of data */ +#define BUF_POINT_INFO 0xE0 -/* commands and their subcommands. when no subcommands exist, a zero is send as the second byte */ +/* + * commands and their subcommands. when no subcommands exist, a zero + * is send as the second byte + */ #define CMD_IDENT_CHIP 0x00 -#define CMD_READ_VERSIONS 0x01 /* VERSION_LENGTH bytes of data in response */ -# define VER_FIRMWARE 0x00 -# define VER_CONFIG 0x06 -# define VERSION_LENGTH 10 -#define CMD_PWR_CTL 0x04 /* subcommand is zero, next byte is power mode */ -# define PWR_CTL_LOW_POWER_MODE 0x01 /* idle mode */ -# define PWR_CTL_SLEEP_MODE 0x02 /* sleep mode */ -#define CMD_UNKNOWN_7 0x07 /* command is not documented in the datasheet v1.0.0.7 */ +/* VERSION_LENGTH bytes of data in response */ +#define CMD_READ_VERSIONS 0x01 +#define VER_FIRMWARE 0x00 +#define VER_CONFIG 0x06 +#define VERSION_LENGTH 10 +/* subcommand is zero, next byte is power mode */ +#define CMD_PWR_CTL 0x04 +/* idle mode */ +#define PWR_CTL_LOW_POWER_MODE 0x01 +/* sleep mode */ +#define PWR_CTL_SLEEP_MODE 0x02 +/* command is not documented in the datasheet v1.0.0.7 */ +#define CMD_UNKNOWN_7 0x07 #define CMD_FIRMWARE_REINIT_C 0x0C -#define CMD_CALIBRATE 0x13 /* needs to be followed by 4 bytes of zeroes */ +/* needs to be followed by 4 bytes of zeroes */ +#define CMD_CALIBRATE 0x13 #define CMD_FIRMWARE_UPGRADE 0x60 -# define FIRMWARE_MODE_ENTER 0x00 -# define FIRMWARE_MODE_EXIT 0x80 -#define CMD_SET_START_OFFSET 0x61 /* address for FW read/write */ -#define CMD_FW_WRITE 0x62 /* subcommand is number of bytes to write */ -#define CMD_FW_READ 0x63 /* subcommand is number of bytes to read */ +#define FIRMWARE_MODE_ENTER 0x00 +#define FIRMWARE_MODE_EXIT 0x80 +/* address for FW read/write */ +#define CMD_SET_START_OFFSET 0x61 +/* subcommand is number of bytes to write */ +#define CMD_FW_WRITE 0x62 +/* subcommand is number of bytes to read */ +#define CMD_FW_READ 0x63 #define CMD_FIRMWARE_REINIT_6F 0x6F #define FW_WRITE_CHUNK_SIZE 128 @@ -72,7 +92,8 @@ #define PT_INFO_BITS 0xF8 #define BT_INFO_NONE 0x00 #define PT_INFO_YES 0x80 -#define BT_INFO_NONE_BUT_DOWN 0x08 /* no new data but finder(s) still down */ +/* no new data but finder(s) still down */ +#define BT_INFO_NONE_BUT_DOWN 0x08 /* use this to include integers in commands */ #define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8)) @@ -83,19 +104,21 @@ struct FingerData { uint8_t hi; uint8_t yLo; uint8_t pressure; -} __attribute__((packed)); +} __packed; struct PointData { uint8_t flags; uint8_t palm; struct FingerData fd[3]; -} __attribute__((packed)); +} __packed; #define PD_FLAGS_DATA_TYPE_BITS 0xF0 /* other types (like chip-detected gestures) exist but we do not care */ #define PD_FLAGS_DATA_TYPE_TOUCH 0x00 -#define PD_FLAGS_NOT_PEN 0x08 /* set if pen touched, clear if finger(s) */ -#define PD_FLAGS_HAVE_FINGERS 0x07 /* a bit for each finger data that is valid (from lsb to msb) */ +/* set if pen touched, clear if finger(s) */ +#define PD_FLAGS_NOT_PEN 0x08 +/* a bit for each finger data that is valid (from lsb to msb) */ +#define PD_FLAGS_HAVE_FINGERS 0x07 #define PD_PALM_FLAG_BIT 0x01 #define FD_PRESSURE_BITS 0x0F #define FD_PRESSURE_NONE 0x00 @@ -110,14 +133,13 @@ struct IT7260_ts_data { struct input_dev *input_dev; }; -static int8_t fwUploadResult = SYSFS_RESULT_NOT_DONE; -static int8_t calibrationWasSuccessful = SYSFS_RESULT_NOT_DONE; -static bool devicePresent = false; +static int8_t fwUploadResult; +static int8_t calibrationWasSuccessful; +static bool devicePresent; static DEFINE_MUTEX(sleepModeMutex); -static bool chipAwake = true; -static bool hadFingerDown = false; -static bool isDeviceSleeping = false; -static bool isDeviceSuspend = false; +static bool chipAwake; +static bool hadFingerDown; +static bool isDeviceSuspend; static struct input_dev *input_dev; static struct IT7260_ts_data *gl_ts; @@ -125,7 +147,8 @@ static struct IT7260_ts_data *gl_ts; #define LOGI(...) printk(DEVICE_NAME ": " __VA_ARGS__) /* internal use func - does not make sure chip is ready before read */ -static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength) +static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, + uint16_t dataLength) { struct i2c_msg msgs[2] = { { @@ -147,7 +170,8 @@ static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16 return i2c_transfer(gl_ts->client->adapter, msgs, 2); } -static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength) +static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, + const uint8_t *dataBuffer, uint16_t dataLength) { uint8_t txbuf[257]; struct i2c_msg msg = { @@ -167,16 +191,16 @@ static bool i2cWriteNoReadyCheck(uint8_t bufferIndex, const uint8_t *dataBuffer, } /* - * Device is apparently always ready for i2c but not for actual register reads/writes. - * This function ascertains it is ready for that too. the results of this call often - * were ignored. + * Device is apparently always ready for i2c but not for actual + * register reads/writes. This function ascertains it is ready + * for that too. the results of this call often were ignored. */ static bool waitDeviceReady(bool forever, bool slowly) { uint8_t ucQuery; uint32_t count = DEVICE_READY_MAX_WAIT; - do{ + do { if (!i2cReadNoReadyCheck(BUF_QUERY, &ucQuery, sizeof(ucQuery))) ucQuery = CMD_STATUS_BUSY; @@ -185,18 +209,20 @@ static bool waitDeviceReady(bool forever, bool slowly) if (!forever) count--; - }while((ucQuery & CMD_STATUS_BUSY) && count); + } while ((ucQuery & CMD_STATUS_BUSY) && count); return !ucQuery; } -static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength) +static bool i2cRead(uint8_t bufferIndex, uint8_t *dataBuffer, + uint16_t dataLength) { waitDeviceReady(false, false); return i2cReadNoReadyCheck(bufferIndex, dataBuffer, dataLength); } -static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer, uint16_t dataLength) +static bool i2cWrite(uint8_t bufferIndex, const uint8_t *dataBuffer, + uint16_t dataLength) { waitDeviceReady(false, false); return i2cWriteNoReadyCheck(bufferIndex, dataBuffer, dataLength); @@ -219,7 +245,8 @@ static bool chipFirmwareReinitialize(uint8_t cmdOfChoice) static bool chipFirmwareUpgradeModeEnterExit(bool enter) { - uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', '6', '0', 0x55, 0xAA}; + uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', + '6', '0', 0x55, 0xAA}; uint8_t resp[2]; cmd[1] = enter ? FIRMWARE_MODE_ENTER : FIRMWARE_MODE_EXIT; @@ -252,11 +279,13 @@ static bool chipSetStartOffset(uint16_t offset) /* write fwLength bytes from fwData at chip offset writeStartOffset */ -static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData, uint16_t writeStartOffset) +static bool chipFlashWriteAndVerify(unsigned int fwLength, + const uint8_t *fwData, uint16_t writeStartOffset) { uint32_t curDataOfst; - for (curDataOfst = 0; curDataOfst < fwLength; curDataOfst += FW_WRITE_CHUNK_SIZE) { + for (curDataOfst = 0; curDataOfst < fwLength; + curDataOfst += FW_WRITE_CHUNK_SIZE) { uint8_t cmdWrite[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE}; uint8_t bufRead[FW_WRITE_CHUNK_SIZE]; @@ -277,9 +306,10 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData /* prepare the read command */ cmdRead[1] = curWriteSz; - for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT; nRetries++) { + for (nRetries = 0; nRetries < FW_WRITE_RETRY_COUNT; + nRetries++) { - /* set write offset and write the data*/ + /* set write offset and write the data */ chipSetStartOffset(writeStartOffset + curDataOfst); i2cWrite(BUF_COMMAND, cmdWrite, 2 + curWriteSz); @@ -289,10 +319,13 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData i2cRead(BUF_RESPONSE, bufRead, curWriteSz); /* verify. If success break out of retry loop */ - for (i = 0; i < curWriteSz && bufRead[i] == cmdWrite[i + 2]; i++); + i = 0; + while (i < curWriteSz && bufRead[i] == cmdWrite[i + 2]) + i++; if (i == curWriteSz) break; - LOGE("write of data offset %u failed on try %u at byte %u/%u\n", curDataOfst, nRetries, i, curWriteSz); + pr_err("write of data offset %u failed on try %u at byte %u/%u\n", + curDataOfst, nRetries, i, curWriteSz); } /* if we've failed after all the retries, tell the caller */ if (nRetries == FW_WRITE_RETRY_COUNT) @@ -302,7 +335,8 @@ static bool chipFlashWriteAndVerify(unsigned int fwLength, const uint8_t *fwData return true; } -static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t cfgLen, const uint8_t *cfgData) +static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, + uint32_t cfgLen, const uint8_t *cfgData) { bool success = false; @@ -317,7 +351,8 @@ static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t c } /* flash config data if requested */ - if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData, CHIP_FLASH_SIZE - cfgLen)) { + if (fwLen && fwData && !chipFlashWriteAndVerify(cfgLen, cfgData, + CHIP_FLASH_SIZE - cfgLen)) { LOGE("failed to upload touch cfg data\n"); goto out; } @@ -325,24 +360,33 @@ static bool chipFirmwareUpload(uint32_t fwLen, const uint8_t *fwData, uint32_t c success = true; out: - return chipFirmwareUpgradeModeEnterExit(false) && chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success; + return chipFirmwareUpgradeModeEnterExit(false) && + chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) && success; } + /* * both buffers should be VERSION_LENGTH in size, * but only a part of them is significant */ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt) { - /* this code to get versions is reproduced as was written, but it does not make sense. Something here *PROBABLY IS* wrong */ + /* + * this code to get versions is reproduced as was written, but it does + * not make sense. Something here *PROBABLY IS* wrong + */ static const uint8_t cmdReadFwVer[] = {CMD_READ_VERSIONS, VER_FIRMWARE}; static const uint8_t cmdReadCfgVer[] = {CMD_READ_VERSIONS, VER_CONFIG}; bool ret = true; - /* this structure is so that we definitely do all the calls, but still return a status in case anyone cares */ + /* + * this structure is so that we definitely do all the calls, but still + * return a status in case anyone cares + */ ret = i2cWrite(BUF_COMMAND, cmdReadFwVer, sizeof(cmdReadFwVer)) && ret; ret = i2cRead(BUF_RESPONSE, verFw, VERSION_LENGTH) && ret; - ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer, sizeof(cmdReadCfgVer)) && ret; + ret = i2cWrite(BUF_COMMAND, cmdReadCfgVer, + sizeof(cmdReadCfgVer)) && ret; ret = i2cRead(BUF_RESPONSE, verCfg, VERSION_LENGTH) && ret; if (logIt) @@ -353,26 +397,28 @@ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt) return ret; } -static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsUpgradeStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { const struct firmware *fw, *cfg; uint8_t verFw[10], verCfg[10]; unsigned fwLen = 0, cfgLen = 0; bool manualUpgrade, success; - int mode = 0; + int mode = 0, ret; - if (request_firmware(&fw, "it7260.fw", dev)) + ret = request_firmware(&fw, "it7260.fw", dev); + if (ret) LOGE("failed to get firmware for it7260\n"); else fwLen = fw->size; - - if (request_firmware(&cfg, "it7260.cfg", dev)) + ret = request_firmware(&cfg, "it7260.cfg", dev); + if (ret) LOGE("failed to get config data for it7260\n"); else cfgLen = cfg->size; - sscanf(buf, "%d", &mode); + ret = kstrtoint(buf, 10, &mode); manualUpgrade = mode == SYSFS_FW_UPLOAD_MODE_MANUAL; LOGI("firmware found %ub of fw and %ub of config in %s mode\n", fwLen, cfgLen, manualUpgrade ? "manual" : "normal"); @@ -389,13 +435,14 @@ static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *at verCfg[4] < cfg->data[cfgLen - 5])){ LOGI("firmware/config will be upgraded\n"); disable_irq(gl_ts->client->irq); - success = chipFirmwareUpload(fwLen, fw->data, cfgLen, cfg->data); + success = chipFirmwareUpload(fwLen, fw->data, cfgLen, + cfg->data); enable_irq(gl_ts->client->irq); - fwUploadResult = success ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; + fwUploadResult = success ? + SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; LOGI("upload %s\n", success ? "success" : "failed"); - } - else { + } else { LOGI("firmware/config upgrade not needed\n"); } } @@ -409,14 +456,16 @@ static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *at return count; } -static ssize_t sysfsUpgradeShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsUpgradeShow(struct device *dev, + struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d", fwUploadResult); + return snprintf(buf, MAX_BUFFER_SIZE, "%d", fwUploadResult); } -static ssize_t sysfsCalibrationShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsCalibrationShow(struct device *dev, + struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d", calibrationWasSuccessful); + return snprintf(buf, MAX_BUFFER_SIZE, "%d", calibrationWasSuccessful); } static bool chipSendCalibrationCmd(bool autoTuneOn) @@ -425,52 +474,68 @@ static bool chipSendCalibrationCmd(bool autoTuneOn) return i2cWrite(BUF_COMMAND, cmdCalibrate, sizeof(cmdCalibrate)); } -static ssize_t sysfsCalibrationStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsCalibrationStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { uint8_t resp; if (!chipSendCalibrationCmd(false)) LOGE("failed to send calibration command\n"); else { - calibrationWasSuccessful = i2cRead(BUF_RESPONSE, &resp, sizeof(resp)) ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; - - /* previous logic that was here never called chipFirmwareReinitialize() due to checking a guaranteed-not-null value against null. We now call it. Hopefully this is OK */ + calibrationWasSuccessful = + i2cRead(BUF_RESPONSE, &resp, sizeof(resp)) + ? SYSFS_RESULT_SUCCESS : SYSFS_RESULT_FAIL; + + /* + * previous logic that was here never called + * chipFirmwareReinitialize() due to checking a + * guaranteed-not-null value against null. We now + * call it. Hopefully this is OK + */ if (!resp) - LOGI("chipFirmwareReinitialize -> %s\n", chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) ? "success" : "fail"); + LOGI("chipFirmwareReinitialize -> %s\n", + chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_6F) + ? "success" : "fail"); } return count; } -static ssize_t sysfsPointShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsPointShow(struct device *dev, + struct device_attribute *attr, char *buf) { uint8_t pointData[sizeof(struct PointData)]; bool readSuccess; ssize_t ret; - readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData, sizeof(pointData)); - ret = sprintf(buf, "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n", - readSuccess, pointData[0],pointData[1],pointData[2],pointData[3], - pointData[4],pointData[5],pointData[6],pointData[7],pointData[8], - pointData[9],pointData[10],pointData[11],pointData[12],pointData[13]); - + readSuccess = i2cReadNoReadyCheck(BUF_POINT_INFO, pointData, + sizeof(pointData)); + ret = snprintf(buf, MAX_BUFFER_SIZE, + "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]=\n", + readSuccess, pointData[0], pointData[1], pointData[2], + pointData[3], pointData[4], pointData[5], pointData[6], + pointData[7], pointData[8], pointData[9], pointData[10], + pointData[11], pointData[12], pointData[13]); LOGI("%s", buf); return ret; } -static ssize_t sysfsPointStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsPointStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { return count; } -static ssize_t sysfsStatusShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsStatusShow(struct device *dev, + struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", devicePresent ? 1 : 0); + return snprintf(buf, MAX_BUFFER_SIZE, "%d\n", devicePresent ? 1 : 0); } -static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsStatusStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { uint8_t verFw[10], verCfg[10]; @@ -479,68 +544,82 @@ static ssize_t sysfsStatusStore(struct device *dev, struct device_attribute *att return count; } -static ssize_t sysfsVersionShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsVersionShow(struct device *dev, + struct device_attribute *attr, char *buf) { uint8_t verFw[10], verCfg[10]; chipGetVersions(verFw, verCfg, false); - return sprintf(buf, "%x,%x,%x,%x # %x,%x,%x,%x\n",verFw[5], verFw[6], verFw[7], verFw[8], verCfg[1], verCfg[2], verCfg[3], verCfg[4]); + return snprintf(buf, MAX_BUFFER_SIZE, "%x,%x,%x,%x # %x,%x,%x,%x\n", + verFw[5], verFw[6], verFw[7], verFw[8], + verCfg[1], verCfg[2], verCfg[3], verCfg[4]); } -static ssize_t sysfsVersionStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsVersionStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { return count; } -static ssize_t sysfsSleepShow(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sysfsSleepShow(struct device *dev, + struct device_attribute *attr, char *buf) { /* - * The usefulness of this was questionable at best - we were at least leaking a byte of - * kernel data (by claiming to return a byte but not writing to buf. To fix this now - * we actually return the sleep status + * The usefulness of this was questionable at best - we were at least + * leaking a byte of kernel data (by claiming to return a byte but not + * writing to buf. To fix this now we actually return the sleep status */ if (!mutex_lock_interruptible(&sleepModeMutex)) { *buf = chipAwake ? '1' : '0'; mutex_unlock(&sleepModeMutex); return 1; - } - else + } else { return -EINTR; + } } -static ssize_t sysfsSleepStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t sysfsSleepStore(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { - static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, 0x00, PWR_CTL_SLEEP_MODE}; - int goToSleepVal; + static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, + 0x00, PWR_CTL_SLEEP_MODE}; + int goToSleepVal, ret; bool goToWake; uint8_t dummy; - sscanf(buf, "%d", &goToSleepVal); - goToWake = !goToSleepVal; /* convert to bool of proper polarity */ + ret = kstrtoint(buf, 10, &goToSleepVal); + /* convert to bool of proper polarity */ + goToWake = !goToSleepVal; if (!mutex_lock_interruptible(&sleepModeMutex)) { if ((chipAwake && goToWake) || (!chipAwake && !goToWake)) - LOGE("duplicate request to %s chip\n", goToWake ? "wake" : "sleep"); + LOGE("duplicate request to %s chip\n", + goToWake ? "wake" : "sleep"); else if (goToWake) { i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); enable_irq(gl_ts->client->irq); - LOGI("touch is going to wake!\n\n"); + LOGI("touch is going to wake!\n"); } else { disable_irq(gl_ts->client->irq); - i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, sizeof(cmdGoSleep)); - LOGI("touch is going to sleep...\n\n"); + i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, + sizeof(cmdGoSleep)); + LOGI("touch is going to sleep...\n"); } chipAwake = goToWake; mutex_unlock(&sleepModeMutex); return count; - } else + } else { return -EINTR; + } } -static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, sysfsStatusShow, sysfsStatusStore); -static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP, sysfsVersionShow, sysfsVersionStore); -static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP, sysfsSleepShow, sysfsSleepStore); +static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsStatusShow, sysfsStatusStore); +static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsVersionShow, sysfsVersionStore); +static DEVICE_ATTR(sleep, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsSleepShow, sysfsSleepStore); static struct attribute *it7260_attrstatus[] = { &dev_attr_status.attr, @@ -553,10 +632,12 @@ static const struct attribute_group it7260_attrstatus_group = { .attrs = it7260_attrstatus, }; -static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP, sysfsCalibrationShow, sysfsCalibrationStore); -static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP, sysfsUpgradeShow, sysfsUpgradeStore); -static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP, sysfsPointShow, sysfsPointStore); - +static DEVICE_ATTR(calibration, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsCalibrationShow, sysfsCalibrationStore); +static DEVICE_ATTR(upgrade, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsUpgradeShow, sysfsUpgradeStore); +static DEVICE_ATTR(point, S_IRUGO|S_IWUSR|S_IWGRP, + sysfsPointShow, sysfsPointStore); static struct attribute *it7260_attributes[] = { &dev_attr_calibration.attr, @@ -573,7 +654,8 @@ static void chipExternalCalibration(bool autoTuneEnabled) { uint8_t resp[2]; - LOGI("sent calibration command -> %d\n", chipSendCalibrationCmd(autoTuneEnabled)); + LOGI("sent calibration command -> %d\n", + chipSendCalibrationCmd(autoTuneEnabled)); waitDeviceReady(true, true); i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp)); chipFirmwareReinitialize(CMD_FIRMWARE_REINIT_C); @@ -585,7 +667,8 @@ void sendCalibrationCmd(void) } EXPORT_SYMBOL(sendCalibrationCmd); -static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, const struct FingerData *fd) +static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, + const struct FingerData *fd) { uint16_t x = fd->xLo; uint16_t y = fd->yLo; @@ -611,15 +694,19 @@ static void readTouchDataPoint(void) /* verify there is point data to read & it is readable and valid */ i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus)); if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) { - LOGE("readTouchDataPoint() called when no data available (0x%02X)\n", devStatus); + pr_err("readTouchDataPoint() called when no data available (0x%02X)\n", + devStatus); return; } - if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void*)&pointData, sizeof(pointData))) { - LOGE("readTouchDataPoint() failed to read point data buffer\n"); + if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData, + sizeof(pointData))) { + pr_err("readTouchDataPoint() failed to read point data buffer\n"); return; } - if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != PD_FLAGS_DATA_TYPE_TOUCH) { - LOGE("readTouchDataPoint() dropping non-point data of type 0x%02X\n", pointData.flags); + if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != + PD_FLAGS_DATA_TYPE_TOUCH) { + pr_err("readTouchDataPoint() dropping non-point data of type 0x%02X\n", + pointData.flags); return; } @@ -649,20 +736,15 @@ static void readTouchDataPoint(void) static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) { - smp_rmb(); - if (isDeviceSleeping) { - smp_wmb(); - } else { - readTouchDataPoint(); - } - + readTouchDataPoint(); return IRQ_HANDLED; } static bool chipIdentifyIT7260(void) { static const uint8_t cmdIdent[] = {CMD_IDENT_CHIP}; - static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7', '2', '6', '0'}; + static const uint8_t expectedID[] = {0x0A, 'I', 'T', 'E', '7', + '2', '6', '0'}; uint8_t chipID[10] = {0,}; waitDeviceReady(true, false); @@ -678,7 +760,7 @@ static bool chipIdentifyIT7260(void) LOGE("i2cRead() failed\n"); return false; } - pr_info("chipIdentifyIT7260 read id: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + LOGI("chipIdentifyIT7260 read id: %02X %c%c%c%c%c%c%ci %c%c\n", chipID[0], chipID[1], chipID[2], chipID[3], chipID[4], chipID[5], chipID[6], chipID[7], chipID[8], chipID[9]); @@ -690,12 +772,14 @@ static bool chipIdentifyIT7260(void) else if (chipID[8] == '6' && chipID[9] == '6') LOGI("rev BX4 found\n"); else - LOGI("unknown revision (0x%02X 0x%02X) found\n", chipID[8], chipID[9]); + LOGI("unknown revision (0x%02X 0x%02X) found\n", + chipID[8], chipID[9]); return true; } -static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int IT7260_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) { static const uint8_t cmdStart[] = {CMD_UNKNOWN_7}; struct IT7260_i2c_platform_data *pdata; @@ -729,7 +813,7 @@ static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id } if (!chipIdentifyIT7260()) { - LOGI ("chipIdentifyIT7260 FAIL"); + LOGI("chipIdentifyIT7260 FAIL"); goto err_ident_fail_or_input_alloc; } @@ -762,7 +846,8 @@ static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id goto err_input_register; } - if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) { + if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) { dev_err(&client->dev, "request_irq failed\n"); goto err_irq_reg; } diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index c78178c0e1a1..a0227fd05939 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -29,6 +29,8 @@ #include <asm/cacheflush.h> #include <asm/dma-iommu.h> +#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS) + static const char *iommu_debug_attr_to_string(enum iommu_attr attr) { switch (attr) { @@ -74,6 +76,7 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr) return "Unknown attr!"; } } +#endif #ifdef CONFIG_IOMMU_DEBUG_TRACKING @@ -457,6 +460,20 @@ static inline void iommu_debug_destroy_tracking(void) { } #ifdef CONFIG_IOMMU_TESTS +#ifdef CONFIG_64BIT + +#define kstrtoux kstrtou64 +#define kstrtox_from_user kstrtoll_from_user +#define kstrtosize_t kstrtoul + +#else + +#define kstrtoux kstrtou32 +#define kstrtox_from_user kstrtoint_from_user +#define kstrtosize_t kstrtouint + +#endif + static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; @@ -549,10 +566,10 @@ DEFINE_SIMPLE_ATTRIBUTE(iommu_debug_nr_iters_ops, static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, enum iommu_attr attrs[], void *attr_values[], int nattrs, - const unsigned long sizes[]) + const size_t sizes[]) { int i; - const unsigned long *sz; + const size_t *sz; struct iommu_domain *domain; struct bus_type *bus; unsigned long iova = 0x10000; @@ -593,7 +610,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, seq_printf(s, "(average over %d iterations)\n", iters_per_op); seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map", "iommu_unmap"); for (sz = sizes; *sz; ++sz) { - unsigned long size = *sz; + size_t size = *sz; size_t unmapped; u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0; u64 map_elapsed_us = 0, unmap_elapsed_us = 0; @@ -625,8 +642,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, unmap_elapsed_ns += timespec_to_ns(&diff); } - map_elapsed_ns /= iters_per_op; - unmap_elapsed_ns /= iters_per_op; + map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op, + &map_elapsed_rem); + unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op, + &unmap_elapsed_rem); map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000, &map_elapsed_rem); @@ -642,7 +661,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, seq_putc(s, '\n'); seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map_sg", "iommu_unmap"); for (sz = sizes; *sz; ++sz) { - unsigned long size = *sz; + size_t size = *sz; size_t unmapped; u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0; u64 map_elapsed_us = 0, unmap_elapsed_us = 0; @@ -683,8 +702,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, unmap_elapsed_ns += timespec_to_ns(&diff); } - map_elapsed_ns /= iters_per_op; - unmap_elapsed_ns /= iters_per_op; + map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op, + &map_elapsed_rem); + unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op, + &unmap_elapsed_rem); map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000, &map_elapsed_rem); @@ -709,7 +730,7 @@ out_domain_free: static int iommu_debug_profiling_show(struct seq_file *s, void *ignored) { struct iommu_debug_device *ddev = s->private; - const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, + const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, SZ_1M * 20, 0 }; enum iommu_attr attrs[] = { DOMAIN_ATTR_COHERENT_HTW_DISABLE, @@ -739,7 +760,7 @@ static const struct file_operations iommu_debug_profiling_fops = { static int iommu_debug_secure_profiling_show(struct seq_file *s, void *ignored) { struct iommu_debug_device *ddev = s->private; - const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, + const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, SZ_1M * 20, 0 }; enum iommu_attr attrs[] = { @@ -830,7 +851,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, if (!virt) goto out; - mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL); + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL); if (!mapping) { seq_puts(s, "fast_smmu_create_mapping failed\n"); goto out_kfree; @@ -851,7 +872,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, goto out_detach; } for (experiment = 0; experiment < 2; ++experiment) { - u64 map_avg = 0, unmap_avg = 0; + size_t map_avg = 0, unmap_avg = 0; for (i = 0; i < 10; ++i) { struct timespec tbefore, tafter, diff; @@ -888,7 +909,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, i < 9 ? ", " : ""); } map_avg /= 10; - seq_printf(s, "] (avg: %llu)\n", map_avg); + seq_printf(s, "] (avg: %zu)\n", map_avg); seq_printf(s, "%13s %24s (ns): [", extra_labels[experiment], "dma_unmap_single_attrs"); @@ -898,7 +919,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, i < 9 ? ", " : ""); } unmap_avg /= 10; - seq_printf(s, "] (avg: %llu)\n", unmap_avg); + seq_printf(s, "] (avg: %zu)\n", unmap_avg); } out_disable_config_clocks: @@ -1382,7 +1403,7 @@ static int __apply_to_new_mapping(struct seq_file *s, int ret = -EINVAL, fast = 1; phys_addr_t pt_phys; - mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL); + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL); if (!mapping) goto out; @@ -1631,7 +1652,7 @@ static ssize_t iommu_debug_atos_write(struct file *file, struct iommu_debug_device *ddev = file->private_data; dma_addr_t iova; - if (kstrtoll_from_user(ubuf, count, 0, &iova)) { + if (kstrtox_from_user(ubuf, count, 0, &iova)) { pr_err("Invalid format for iova\n"); ddev->iova = 0; return -EINVAL; @@ -1730,13 +1751,13 @@ static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, /* split up the words */ *comma1 = *comma2 = *comma3 = '\0'; - if (kstrtou64(buf, 0, &iova)) + if (kstrtoux(buf, 0, &iova)) goto invalid_format; - if (kstrtou64(comma1 + 1, 0, &phys)) + if (kstrtoux(comma1 + 1, 0, &phys)) goto invalid_format; - if (kstrtoul(comma2 + 1, 0, &size)) + if (kstrtosize_t(comma2 + 1, 0, &size)) goto invalid_format; if (kstrtoint(comma3 + 1, 0, &prot)) @@ -1802,10 +1823,10 @@ static ssize_t iommu_debug_unmap_write(struct file *file, /* split up the words */ *comma1 = '\0'; - if (kstrtou64(buf, 0, &iova)) + if (kstrtoux(buf, 0, &iova)) goto invalid_format; - if (kstrtoul(comma1 + 1, 0, &size)) + if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; unmapped = iommu_unmap(ddev->domain, iova, size); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c index 57e389b4c497..b8840115b674 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c @@ -1009,15 +1009,9 @@ static int msm_jpegdma_s_crop(struct file *file, void *fh, if (crop->c.width % formats[ctx->format_idx].h_align) return -EINVAL; - if (crop->c.left % formats[ctx->format_idx].h_align) - return -EINVAL; - if (crop->c.height % formats[ctx->format_idx].v_align) return -EINVAL; - if (crop->c.top % formats[ctx->format_idx].v_align) - return -EINVAL; - ctx->crop = crop->c; if (atomic_read(&ctx->active)) ret = msm_jpegdma_update_hw_config(ctx); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h index 0a9cab6e4322..6a1205daf1d2 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h @@ -36,9 +36,9 @@ /* Dma input output size limitations */ #define MSM_JPEGDMA_MAX_WIDTH 65536 -#define MSM_JPEGDMA_MIN_WIDTH 32 +#define MSM_JPEGDMA_MIN_WIDTH 8 #define MSM_JPEGDMA_MAX_HEIGHT 65536 -#define MSM_JPEGDMA_MIN_HEIGHT 32 +#define MSM_JPEGDMA_MIN_HEIGHT 8 #define MSM_JPEGDMA_STRIDE_ALIGN 8 /* diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index d58684109395..4a26ab920016 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -2116,6 +2116,14 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32) + sizeof(struct hfi_enable); break; } + case HAL_PARAM_VENC_H264_TRANSFORM_8x8: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HAL_CONFIG_BUFFER_REQUIREMENTS: case HAL_CONFIG_PRIORITY: diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 0e668b93598f..5c7408740e95 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -869,7 +869,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .name = "Ltr Mode", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, - .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC, + .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL, .default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, .step = 1, .qmenu = NULL, @@ -1232,6 +1232,15 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .default_value = 0, .step = 1, }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8, + .name = "Transform 8x8", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .step = 1, + }, }; #define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls) @@ -3100,6 +3109,24 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &enable; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8: + property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8; + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE: + enable.enable = 1; + break; + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE: + enable.enable = 0; + break; + default: + dprintk(VIDC_ERR, + "Invalid H264 8x8 transform control value %d\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; default: dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id); rc = -ENOTSUPP; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 624fd53debe8..b2231869c499 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -237,6 +237,7 @@ enum hal_property { HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, HAL_CONFIG_VENC_BLUR_RESOLUTION, HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED, + HAL_PARAM_VENC_H264_TRANSFORM_8x8, }; enum hal_domain { diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index a2d95927e400..ff043e9a819b 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -364,6 +364,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022) #define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023) +#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025) #define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) #define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \ diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 8b8c9a22360b..2141124a6c12 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/proc_fs.h> -#include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/stat.h> @@ -39,44 +38,65 @@ struct uid_stat { }; static struct uid_stat *find_uid_stat(uid_t uid) { + unsigned long flags; struct uid_stat *entry; + spin_lock_irqsave(&uid_lock, flags); list_for_each_entry(entry, &uid_list, link) { if (entry->uid == uid) { + spin_unlock_irqrestore(&uid_lock, flags); return entry; } } + spin_unlock_irqrestore(&uid_lock, flags); return NULL; } -static int uid_stat_atomic_int_show(struct seq_file *m, void *v) +static int tcp_snd_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int len; unsigned int bytes; - atomic_t *counter = m->private; - - bytes = (unsigned int) (atomic_read(counter) + INT_MIN); - seq_printf(m, "%u\n", bytes); - return seq_has_overflowed(m) ? -ENOSPC : 0; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; } -static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) +static int tcp_rcv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { - return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; } -static const struct file_operations uid_stat_read_atomic_int_fops = { - .open = uid_stat_read_atomic_int_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* Create a new entry for tracking the specified uid. */ static struct uid_stat *create_stat(uid_t uid) { + unsigned long flags; + char uid_s[32]; struct uid_stat *new_uid; + struct proc_dir_entry *entry; + /* Create the uid stat struct and append it to the list. */ - new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); - if (!new_uid) + if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) return NULL; new_uid->uid = uid; @@ -84,48 +104,30 @@ static struct uid_stat *create_stat(uid_t uid) { atomic_set(&new_uid->tcp_rcv, INT_MIN); atomic_set(&new_uid->tcp_snd, INT_MIN); + spin_lock_irqsave(&uid_lock, flags); list_add_tail(&new_uid->link, &uid_list); - return new_uid; -} + spin_unlock_irqrestore(&uid_lock, flags); -static void create_stat_proc(struct uid_stat *new_uid) -{ - char uid_s[32]; - struct proc_dir_entry *entry; - sprintf(uid_s, "%d", new_uid->uid); + sprintf(uid_s, "%d", uid); entry = proc_mkdir(uid_s, parent); /* Keep reference to uid_stat so we know what uid to read stats from. */ - proc_create_data("tcp_snd", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); + create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, + (void *) new_uid); - proc_create_data("tcp_rcv", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); -} + create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, + (void *) new_uid); -static struct uid_stat *find_or_create_uid_stat(uid_t uid) -{ - struct uid_stat *entry; - unsigned long flags; - spin_lock_irqsave(&uid_lock, flags); - entry = find_uid_stat(uid); - if (entry) { - spin_unlock_irqrestore(&uid_lock, flags); - return entry; - } - entry = create_stat(uid); - spin_unlock_irqrestore(&uid_lock, flags); - if (entry) - create_stat_proc(entry); - return entry; + return new_uid; } int uid_stat_tcp_snd(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } atomic_add(size, &entry->tcp_snd); return 0; } @@ -133,9 +135,10 @@ int uid_stat_tcp_snd(uid_t uid, int size) { int uid_stat_tcp_rcv(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } atomic_add(size, &entry->tcp_rcv); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index d7a987335dda..8e583203abda 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -162,6 +162,12 @@ struct ipa3_usb_transport_type_ctx { void *user_data; enum ipa3_usb_state state; struct finish_suspend_work_context finish_suspend_work; + struct ipa_usb_xdci_chan_params ch_params; +}; + +struct ipa3_usb_smmu_reg_map { + int cnt; + phys_addr_t addr; }; struct ipa3_usb_context { @@ -179,6 +185,7 @@ struct ipa3_usb_context { ttype_ctx[IPA_USB_TRANSPORT_MAX]; struct dentry *dfile_state_info; struct dentry *dent; + struct ipa3_usb_smmu_reg_map smmu_reg_map; }; enum ipa3_usb_op { @@ -1061,8 +1068,8 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params) IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len); IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n", params->xfer_ring_base_addr); - IPA_USB_DBG_LOW("last_trb_addr = %x\n", - params->xfer_scratch.last_trb_addr); + IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n", + params->xfer_scratch.last_trb_addr_iova); IPA_USB_DBG_LOW("const_buffer_size = %d\n", params->xfer_scratch.const_buffer_size); IPA_USB_DBG_LOW("depcmd_low_addr = %x\n", @@ -1112,6 +1119,74 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params) return true; } +static int ipa3_usb_smmu_map_xdci_channel( + struct ipa_usb_xdci_chan_params *params, bool map) +{ + int result; + u32 gevntcount_r = rounddown(params->gevntcount_low_addr, PAGE_SIZE); + u32 xfer_scratch_r = + rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE); + + if (gevntcount_r != xfer_scratch_r) { + IPA_USB_ERR("No support more than 1 page map for USB regs\n"); + WARN_ON(1); + return -EINVAL; + } + + if (map) { + if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) { + ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r; + result = ipa3_smmu_map_peer_reg( + ipa3_usb_ctx->smmu_reg_map.addr, true); + if (result) { + IPA_USB_ERR("failed to map USB regs %d\n", + result); + return result; + } + } else { + if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) { + IPA_USB_ERR( + "No support for map different reg\n"); + return -EINVAL; + } + } + ipa3_usb_ctx->smmu_reg_map.cnt++; + } else { + if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) { + IPA_USB_ERR( + "No support for map different reg\n"); + return -EINVAL; + } + + if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) { + result = ipa3_smmu_map_peer_reg( + ipa3_usb_ctx->smmu_reg_map.addr, false); + if (result) { + IPA_USB_ERR("failed to unmap USB regs %d\n", + result); + return result; + } + } + ipa3_usb_ctx->smmu_reg_map.cnt--; + } + + result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova, + params->xfer_ring_base_addr, params->xfer_ring_len, map); + if (result) { + IPA_USB_ERR("failed to map Xfer ring %d\n", result); + return result; + } + + result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova, + params->data_buff_base_addr, params->data_buff_base_len, map); + if (result) { + IPA_USB_ERR("failed to map TRBs buff %d\n", result); + return result; + } + + return 0; +} + static int ipa3_usb_request_xdci_channel( struct ipa_usb_xdci_chan_params *params, struct ipa_req_chan_out_params *out_params) @@ -1186,6 +1261,16 @@ static int ipa3_usb_request_xdci_channel( default: break; } + + result = ipa3_usb_smmu_map_xdci_channel(params, true); + if (result) { + IPA_USB_ERR("failed to smmu map %d\n", result); + return result; + } + + /* store channel params for SMMU unmap */ + ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params; + chan_params.keep_ipa_awake = params->keep_ipa_awake; chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV; chan_params.evt_ring_params.intr = GSI_INTR_IRQ; @@ -1227,7 +1312,7 @@ static int ipa3_usb_request_xdci_channel( chan_params.chan_params.err_cb = ipa3_usb_gsi_chan_err_cb; chan_params.chan_params.chan_user_data = NULL; chan_params.chan_scratch.xdci.last_trb_addr = - params->xfer_scratch.last_trb_addr; + params->xfer_scratch.last_trb_addr_iova; /* xferrscidx will be updated later */ chan_params.chan_scratch.xdci.xferrscidx = 0; chan_params.chan_scratch.xdci.const_buffer_size = @@ -1243,6 +1328,7 @@ static int ipa3_usb_request_xdci_channel( result = ipa3_request_gsi_channel(&chan_params, out_params); if (result) { IPA_USB_ERR("failed to allocate GSI channel\n"); + ipa3_usb_smmu_map_xdci_channel(params, false); return result; } @@ -1273,6 +1359,9 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl, return result; } + result = ipa3_usb_smmu_map_xdci_channel( + &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false); + /* Change ipa_usb state to INITIALIZED */ if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype)) IPA_USB_ERR("failed to change state to initialized\n"); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 1f9fd7a38a37..cb808bd2a8b7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1524,7 +1524,7 @@ static void ipa_free_buffer(void *user1, int user2) kfree(user1); } -static int ipa_q6_pipe_delay(bool zip_pipes) +int ipa_q6_pipe_delay(bool zip_pipes) { u32 reg_val = 0; int client_idx; @@ -1911,14 +1911,14 @@ int ipa_q6_pre_shutdown_cleanup(void) BUG(); IPA_ACTIVE_CLIENTS_INC_SPECIAL("Q6"); + /* - * pipe delay and holb discard for ZIP pipes are handled - * in post shutdown callback. + * Do not delay Q6 pipes here. This may result in IPA reading a + * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this + * situation IPA will be remain locked as the DMA_TASK with unlock + * bit will not be read by IPA as pipe delay is enabled. IPA uC will + * wait for pipe to be empty before issuing a BAM pipe reset. */ - if (ipa_q6_pipe_delay(false)) { - IPAERR("Failed to delay Q6 pipes\n"); - BUG(); - } if (ipa_q6_monitor_holb_mitigation(false)) { IPAERR("Failed to disable HOLB monitroing on Q6 pipes\n"); @@ -1958,13 +1958,13 @@ int ipa_q6_post_shutdown_cleanup(void) int res; /* - * pipe delay and holb discard for ZIP pipes are handled in - * post shutdown. + * Do not delay Q6 pipes here. This may result in IPA reading a + * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this + * situation IPA will be remain locked as the DMA_TASK with unlock + * bit will not be read by IPA as pipe delay is enabled. IPA uC will + * wait for pipe to be empty before issuing a BAM pipe reset. */ - if (ipa_q6_pipe_delay(true)) { - IPAERR("Failed to delay Q6 ZIP pipes\n"); - BUG(); - } + if (ipa_q6_avoid_holb(true)) { IPAERR("Failed to set HOLB on Q6 ZIP pipes\n"); BUG(); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 7b0376ecba7e..8326c3fdd9d1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1055,6 +1055,83 @@ static bool ipa3_is_legal_params(struct ipa_request_gsi_channel_params *params) return true; } +int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map) +{ + struct iommu_domain *smmu_domain; + int res; + + if (ipa3_ctx->smmu_s1_bypass) + return 0; + + smmu_domain = ipa3_get_smmu_domain(); + if (!smmu_domain) { + IPAERR("invalid smmu domain\n"); + return -EINVAL; + } + + if (map) { + res = ipa3_iommu_map(smmu_domain, phys_addr, phys_addr, + PAGE_SIZE, IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } else { + res = iommu_unmap(smmu_domain, phys_addr, PAGE_SIZE); + res = (res != PAGE_SIZE); + } + if (res) { + IPAERR("Fail to %s reg 0x%pa\n", map ? "map" : "unmap", + &phys_addr); + return -EINVAL; + } + + IPADBG("Peer reg 0x%pa %s\n", &phys_addr, map ? "map" : "unmap"); + + return 0; +} + +int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map) +{ + struct iommu_domain *smmu_domain; + int res; + + if (ipa3_ctx->smmu_s1_bypass) + return 0; + + smmu_domain = ipa3_get_smmu_domain(); + if (!smmu_domain) { + IPAERR("invalid smmu domain\n"); + return -EINVAL; + } + + if (map) { + res = ipa3_iommu_map(smmu_domain, + rounddown(iova, PAGE_SIZE), + rounddown(phys_addr, PAGE_SIZE), + roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE), + IOMMU_READ | IOMMU_WRITE); + if (res) { + IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr); + return -EINVAL; + } + } else { + res = iommu_unmap(smmu_domain, + rounddown(iova, PAGE_SIZE), + roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE)); + if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE)) { + IPAERR("Fail to unmap 0x%llx->0x%pa\n", + iova, &phys_addr); + return -EINVAL; + } + } + + IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap", + iova, &phys_addr); + + return 0; +} + + int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params, struct ipa_req_chan_out_params *out_params) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 82df3768ba26..4c600c6131e9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -1914,6 +1914,7 @@ static void ipa3_replenish_wlan_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = IPA_WLAN_RX_BUFF_SZ; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2101,6 +2102,7 @@ static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2207,6 +2209,7 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2272,6 +2275,7 @@ static void ipa3_fast_replenish_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2407,7 +2411,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb, if (skb->len == 0) { IPAERR("ZLT\n"); - sys->free_skb(skb); return rc; } @@ -2467,7 +2470,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb, sys->prev_skb = skb2; } sys->len_rem -= skb->len; - sys->free_skb(skb); return rc; } } @@ -2481,7 +2483,7 @@ begin: if (skb->len < pkt_status_sz) { WARN_ON(sys->prev_skb != NULL); IPADBG_LOW("status straddles buffer\n"); - sys->prev_skb = skb; + sys->prev_skb = skb_copy(skb, GFP_KERNEL); sys->len_partial = skb->len; return rc; } @@ -2573,7 +2575,7 @@ begin: IPAHAL_PKT_STATUS_EXCEPTION_NONE) { WARN_ON(sys->prev_skb != NULL); IPADBG_LOW("Ins header in next buffer\n"); - sys->prev_skb = skb; + sys->prev_skb = skb_copy(skb, GFP_KERNEL); sys->len_partial = skb->len; return rc; } @@ -2594,7 +2596,7 @@ begin: } skb2 = ipa3_skb_copy_for_client(skb, - status.pkt_len + pkt_status_sz); + min(status.pkt_len + pkt_status_sz, skb->len)); if (likely(skb2)) { if (skb->len < len + pkt_status_sz) { IPADBG_LOW("SPL skb len %d len %d\n", @@ -3764,6 +3766,7 @@ static void ipa_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify) switch (notify->evt_id) { case GSI_CHAN_EVT_EOT: + case GSI_CHAN_EVT_EOB: atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1); if (!atomic_read(&sys->curr_polling_state)) { /* put the gsi channel into polling mode */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index d2c605d7627c..cce05cf31b3c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -2194,4 +2194,7 @@ const char *ipa_hw_error_str(enum ipa3_hw_errors err_type); int ipa_gsi_ch20_wa(void); int ipa3_rx_poll(u32 clnt_hdl, int budget); void ipa3_recycle_wan_skb(struct sk_buff *skb); +int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map); +int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, + u32 size, bool map); #endif /* _IPA3_I_H_ */ diff --git a/drivers/power/qcom-charger/Kconfig b/drivers/power/qcom-charger/Kconfig index b37853b4f70c..7a0b1464ad86 100644 --- a/drivers/power/qcom-charger/Kconfig +++ b/drivers/power/qcom-charger/Kconfig @@ -20,6 +20,16 @@ config QPNP_FG fuel gauge. The state of charge is reported through a BMS power supply property and also sends uevents when the capacity is updated. +config QPNP_FG_GEN3 + tristate "QPNP GEN3 fuel gauge driver" + depends on SPMI + select REGMAP_SPMI + help + Say Y here to enable the GEN3 Fuel Gauge driver. This adds support + for battery fuel gauging and state of charge of battery connected to + the fuel gauge. The state of charge is reported through a BMS power + supply property and also sends uevents when the capacity is updated. + config SMB135X_CHARGER tristate "SMB135X Battery Charger" depends on I2C diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile index df7b78d4fc52..aae6084c3c10 100644 --- a/drivers/power/qcom-charger/Makefile +++ b/drivers/power/qcom-charger/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o obj-$(CONFIG_QPNP_FG) += qpnp-fg.o +obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h new file mode 100644 index 000000000000..cf7869ea1515 --- /dev/null +++ b/drivers/power/qcom-charger/fg-core.h @@ -0,0 +1,247 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __FG_CORE_H__ +#define __FG_CORE_H__ + +#include <linux/atomic.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string_helpers.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include "pmic-voter.h" + +#define fg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info(fmt, ##__VA_ARGS__); \ + else \ + pr_debug(fmt, ##__VA_ARGS__); \ + } while (0) + +#define SRAM_READ "fg_sram_read" +#define SRAM_WRITE "fg_sram_write" +#define SRAM_UPDATE "fg_sram_update" +#define PROFILE_LOAD "fg_profile_load" +#define DELTA_SOC "fg_delta_soc" + +#define DEBUG_PRINT_BUFFER_SIZE 64 +/* 3 byte address + 1 space character */ +#define ADDR_LEN 4 +/* Format is 'XX ' */ +#define CHARS_PER_ITEM 3 +/* 4 data items per line */ +#define ITEMS_PER_LINE 4 +#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * \ + CHARS_PER_ITEM) + 1) \ + +#define FG_SRAM_ADDRESS_MAX 255 + +/* Debug flag definitions */ +enum fg_debug_flag { + FG_IRQ = BIT(0), /* Show interrupts */ + FG_STATUS = BIT(1), /* Show FG status changes */ + FG_POWER_SUPPLY = BIT(2), /* Show POWER_SUPPLY */ + FG_SRAM_WRITE = BIT(3), /* Show SRAM writes */ + FG_SRAM_READ = BIT(4), /* Show SRAM reads */ + FG_BUS_WRITE = BIT(5), /* Show REGMAP writes */ + FG_BUS_READ = BIT(6), /* Show REGMAP reads */ +}; + +/* SRAM access */ +enum sram_access_flags { + FG_IMA_DEFAULT = 0, + FG_IMA_ATOMIC, +}; + +/* JEITA */ +enum { + JEITA_COLD = 0, + JEITA_COOL, + JEITA_WARM, + JEITA_HOT, + NUM_JEITA_LEVELS, +}; + +/* FG irqs */ +enum fg_irq_index { + MSOC_FULL_IRQ = 0, + MSOC_HIGH_IRQ, + MSOC_EMPTY_IRQ, + MSOC_LOW_IRQ, + MSOC_DELTA_IRQ, + BSOC_DELTA_IRQ, + SOC_READY_IRQ, + SOC_UPDATE_IRQ, + BATT_TEMP_DELTA_IRQ, + BATT_MISSING_IRQ, + ESR_DELTA_IRQ, + VBATT_LOW_IRQ, + VBATT_PRED_DELTA_IRQ, + DMA_GRANT_IRQ, + MEM_XCP_IRQ, + IMA_RDY_IRQ, + FG_IRQ_MAX, +}; + +/* WA flags */ +enum { + DELTA_SOC_IRQ_WA = BIT(0), +}; + +/* SRAM parameters */ +enum fg_sram_param_id { + FG_SRAM_BATT_SOC = 0, + FG_SRAM_VOLTAGE_PRED, + FG_SRAM_OCV, + FG_SRAM_RSLOW, + /* Entries below here are configurable during initialization */ + FG_SRAM_CUTOFF_VOLT, + FG_SRAM_EMPTY_VOLT, + FG_SRAM_VBATT_LOW, + FG_SRAM_SYS_TERM_CURR, + FG_SRAM_CHG_TERM_CURR, + FG_SRAM_DELTA_SOC_THR, + FG_SRAM_RECHARGE_SOC_THR, + FG_SRAM_MAX, +}; + +struct fg_sram_param { + u16 address; + int offset; + u8 len; + int value; + int numrtr; + int denmtr; + void (*encode)(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val, u8 *buf); + int (*decode)(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val); +}; + +/* DT parameters for FG device */ +struct fg_dt_props { + int cutoff_volt_mv; + int empty_volt_mv; + int vbatt_low_thr_mv; + int chg_term_curr_ma; + int sys_term_curr_ma; + int delta_soc_thr; + int recharge_soc_thr; + int rsense_sel; + int jeita_thresholds[NUM_JEITA_LEVELS]; +}; + +/* parameters from battery profile */ +struct fg_batt_props { + const char *batt_type_str; + char *batt_profile; + int float_volt_uv; + int fastchg_curr_ma; + int batt_id_kohm; +}; + +struct fg_irq_info { + const char *name; + const irq_handler_t handler; + int irq; + bool wakeable; +}; + +struct fg_chip { + struct device *dev; + struct pmic_revid_data *pmic_rev_id; + struct regmap *regmap; + struct dentry *dentry; + struct power_supply *fg_psy; + struct power_supply *batt_psy; + struct iio_channel *batt_id_chan; + struct fg_memif *sram; + struct fg_irq_info *irqs; + struct votable *awake_votable; + struct fg_sram_param *sp; + int *debug_mask; + char *batt_profile; + struct fg_dt_props dt; + struct fg_batt_props bp; + struct notifier_block nb; + struct mutex bus_lock; + struct mutex sram_rw_lock; + u32 batt_soc_base; + u32 batt_info_base; + u32 mem_if_base; + int nom_cap_uah; + bool batt_id_avail; + bool profile_loaded; + bool battery_missing; + struct completion soc_update; + struct completion soc_ready; + struct delayed_work profile_load_work; + struct work_struct status_change_work; +}; + +/* Debugfs data structures are below */ + +/* Log buffer */ +struct fg_log_buffer { + size_t rpos; + size_t wpos; + size_t len; + char data[0]; +}; + +/* transaction parameters */ +struct fg_trans { + struct fg_chip *chip; + struct fg_log_buffer *log; + u32 cnt; + u16 addr; + u32 offset; + u8 *data; +}; + +struct fg_dbgfs { + struct debugfs_blob_wrapper help_msg; + struct fg_chip *chip; + struct dentry *root; + u32 cnt; + u32 addr; +}; + +extern int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags); +extern int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags); +extern int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset, + u8 mask, u8 val, int flags); +extern int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, + u8 offset, u8 *val, int len); +extern int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, + u8 offset, u8 *val, int len, bool atomic_access); +extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len); +extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len); +extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val); +extern int fg_ima_init(struct fg_chip *chip); +extern int fg_sram_debugfs_create(struct fg_chip *chip); +extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); +extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); +#endif diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c new file mode 100644 index 000000000000..087223d708da --- /dev/null +++ b/drivers/power/qcom-charger/fg-memif.c @@ -0,0 +1,583 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include "fg-core.h" +#include "fg-reg.h" + +/* Generic definitions */ +#define RETRY_COUNT 3 +#define BYTES_PER_SRAM_WORD 4 + +enum { + FG_READ = 0, + FG_WRITE, +}; + +static int fg_set_address(struct fg_chip *chip, u16 address) +{ + u8 buffer[2]; + int rc; + + buffer[0] = address & 0xFF; + /* MSB has to be written zero */ + buffer[1] = 0; + + rc = fg_write(chip, MEM_IF_ADDR_LSB(chip), buffer, 2); + if (rc < 0) { + pr_err("failed to write to 0x%04X, rc=%d\n", + MEM_IF_ADDR_LSB(chip), rc); + return rc; + } + + return rc; +} + +static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst) +{ + int rc; + u8 intf_ctl = 0; + + intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) | + (burst ? MEM_ACS_BURST_BIT : 0); + + rc = fg_masked_write(chip, MEM_IF_IMA_CTL(chip), IMA_CTL_MASK, + intf_ctl); + if (rc < 0) { + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_IMA_CTL(chip), rc); + return -EIO; + } + + return rc; +} + +static int fg_run_iacs_clear_sequence(struct fg_chip *chip) +{ + u8 tmp; + int rc; + + /* + * Values to write for running IACS clear sequence comes from + * hardware documentation. + */ + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, + IACS_CLR_BIT); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), + rc); + return rc; + } + + tmp = 0x4; + rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip), + rc); + return rc; + } + + tmp = 0x0; + rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip), + rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip), + rc); + return rc; + } + + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), + rc); + return rc; + } + + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n"); + return rc; +} + +static int fg_check_for_ima_errors(struct fg_chip *chip) +{ + int rc = 0; + u8 err_sts, exp_sts = 0, hw_sts = 0; + + rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_err_sts rc=%d\n", rc); + return rc; + } + + if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) { + rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_exp_sts rc=%d\n", rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_hw_sts rc=%d\n", rc); + return rc; + } + + pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", + err_sts, exp_sts, hw_sts); + + /* clear the error */ + rc = fg_run_iacs_clear_sequence(chip); + if (rc < 0) { + pr_err("failed to run iacs clear sequence rc=%d\n", rc); + return rc; + } + + /* Retry again as there was an error in the transaction */ + return -EAGAIN; + } + + return rc; +} + +static int fg_check_iacs_ready(struct fg_chip *chip) +{ + int rc = 0, timeout = 250; + u8 ima_opr_sts = 0; + + /* + * Additional delay to make sure IACS ready bit is set after + * Read/Write operation. + */ + + usleep_range(30, 35); + while (1) { + rc = fg_read(chip, MEM_IF_IMA_OPR_STS(chip), &ima_opr_sts, 1); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", + MEM_IF_IMA_OPR_STS(chip), rc); + return rc; + } + + if (ima_opr_sts & IACS_RDY_BIT) + break; + + if (!(--timeout)) + break; + + /* delay for iacs_ready to be asserted */ + usleep_range(5000, 7000); + } + + if (!timeout) { + pr_err("IACS_RDY not set\n"); + + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + return -EBUSY; + } + + return 0; +} + +static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, + int offset, u8 *val, int len) +{ + int rc = 0, i; + u8 *ptr = val, byte_enable = 0, num_bytes = 0; + + fg_dbg(chip, FG_SRAM_WRITE, "length %d addr=%02X offset=%d\n", len, + address, offset); + + while (len > 0) { + num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ? + (BYTES_PER_SRAM_WORD - offset) : len; + + /* write to byte_enable */ + for (i = offset; i < (offset + num_bytes); i++) + byte_enable |= BIT(i); + + rc = fg_write(chip, MEM_IF_IMA_BYTE_EN(chip), &byte_enable, 1); + if (rc < 0) { + pr_err("Unable to write to byte_en_reg rc=%d\n", + rc); + return rc; + } + + /* write data */ + rc = fg_write(chip, MEM_IF_WR_DATA0(chip) + offset, ptr, + num_bytes); + if (rc < 0) { + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_WR_DATA0(chip) + offset, rc); + return rc; + } + + /* + * The last-byte WR_DATA3 starts the write transaction. + * Write a dummy value to WR_DATA3 if it does not have + * valid data. This dummy data is not written to the + * SRAM as byte_en for WR_DATA3 is not set. + */ + if (!(byte_enable & BIT(3))) { + u8 dummy_byte = 0x0; + + rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &dummy_byte, + 1); + if (rc < 0) { + pr_err("failed to write dummy-data to WR_DATA3 rc=%d\n", + rc); + return rc; + } + } + + /* check for error condition */ + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + ptr += num_bytes; + len -= num_bytes; + offset = byte_enable = 0; + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + return rc; +} + +static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address, + int offset, u8 *val, int len) +{ + int rc = 0, total_len; + u8 *rd_data = val, num_bytes; + char str[DEBUG_PRINT_BUFFER_SIZE]; + + fg_dbg(chip, FG_SRAM_READ, "length %d addr=%02X\n", len, address); + + total_len = len; + while (len > 0) { + num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ? + (BYTES_PER_SRAM_WORD - offset) : len; + rc = fg_read(chip, MEM_IF_RD_DATA0(chip) + offset, rd_data, + num_bytes); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", + MEM_IF_RD_DATA0(chip) + offset, rc); + return rc; + } + + rd_data += num_bytes; + len -= num_bytes; + offset = 0; + + /* check for error condition */ + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + if (len && len < BYTES_PER_SRAM_WORD) { + /* + * Move to single mode. Changing address is not + * required here as it must be in burst mode. Address + * will get incremented internally by FG HW once the MSB + * of RD_DATA is read. + */ + rc = fg_config_access_mode(chip, FG_READ, 0); + if (rc < 0) { + pr_err("failed to move to single mode rc=%d\n", + rc); + return -EIO; + } + } + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + if (*chip->debug_mask & FG_SRAM_READ) { + fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); + pr_info("data read: %s\n", str); + } + + return rc; +} + +static int fg_get_mem_access_status(struct fg_chip *chip, bool *status) +{ + int rc; + u8 mem_if_sts; + + rc = fg_read(chip, MEM_IF_MEM_INTF_CFG(chip), &mem_if_sts, 1); + if (rc < 0) { + pr_err("failed to read rif_mem status rc=%d\n", rc); + return rc; + } + + *status = mem_if_sts & MEM_ACCESS_REQ_BIT; + return 0; +} + +static bool is_mem_access_available(struct fg_chip *chip, int access) +{ + bool rif_mem_sts = true; + int rc, time_count = 0; + + while (1) { + rc = fg_get_mem_access_status(chip, &rif_mem_sts); + if (rc < 0) + return rc; + + /* This is an inverting logic */ + if (!rif_mem_sts) + break; + + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "MEM_ACCESS_REQ is not clear yet for IMA_%s\n", + access ? "write" : "read"); + + /* + * Try this no more than 4 times. If MEM_ACCESS_REQ is not + * clear, then return an error instead of waiting for it again. + */ + if (time_count > 4) { + pr_err("Tried 4 times(~16ms) polling MEM_ACCESS_REQ\n"); + return false; + } + + /* Wait for 4ms before reading MEM_ACCESS_REQ again */ + usleep_range(4000, 4100); + time_count++; + } + return true; +} + +static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, + u16 address, int offset, int len, bool access) +{ + int rc = 0; + + if (!is_mem_access_available(chip, access)) + return -EBUSY; + + /* configure for IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT); + if (rc < 0) { + pr_err("failed to set ima_req_access bit rc=%d\n", rc); + return rc; + } + + /* configure for the read/write, single/burst mode */ + rc = fg_config_access_mode(chip, access, (offset + len) > 4); + if (rc < 0) { + pr_err("failed to set memory access rc = %d\n", rc); + return rc; + } + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_err_ratelimited("IACS_RDY failed rc=%d\n", rc); + return rc; + } + + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", rc); + return rc; + } + + if (access == FG_READ) { + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + return rc; +} + +static int fg_get_beat_count(struct fg_chip *chip, u8 *count) +{ + int rc; + + rc = fg_read(chip, MEM_IF_FG_BEAT_COUNT(chip), count, 1); + *count &= BEAT_COUNT_MASK; + return rc; +} + +int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len) +{ + int rc = 0; + u8 start_beat_count, end_beat_count, count = 0; + bool retry_once = false; + + if (offset > 3) { + pr_err("offset too large %d\n", offset); + return -EINVAL; + } + +retry: + rc = fg_interleaved_mem_config(chip, val, address, offset, len, + FG_READ); + if (rc < 0) { + pr_err("failed to configure SRAM for IMA rc = %d\n", rc); + goto out; + } + + /* read the start beat count */ + rc = fg_get_beat_count(chip, &start_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + /* read data */ + rc = __fg_interleaved_mem_read(chip, address, offset, val, len); + if (rc < 0) { + if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { + count++; + pr_err("IMA access failed retry_count = %d\n", count); + goto retry; + } + pr_err("failed to read SRAM address rc = %d\n", rc); + goto out; + } + + /* read the end beat count */ + rc = fg_get_beat_count(chip, &end_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_SRAM_READ, "Start beat_count = %x End beat_count = %x\n", + start_beat_count, end_beat_count); + + if (start_beat_count != end_beat_count && !retry_once) { + fg_dbg(chip, FG_SRAM_READ, "Beat count(%d/%d) do not match - retry transaction\n", + start_beat_count, end_beat_count); + retry_once = true; + } +out: + /* Release IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) { + pr_err("failed to reset IMA access bit rc = %d\n", rc); + return rc; + } + + if (retry_once) + goto retry; + + return rc; +} + +int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, bool atomic_access) +{ + int rc = 0; + u8 start_beat_count, end_beat_count, count = 0; + + if (offset > 3) { + pr_err("offset too large %d\n", offset); + return -EINVAL; + } + +retry: + rc = fg_interleaved_mem_config(chip, val, address, offset, len, + FG_WRITE); + if (rc < 0) { + pr_err("failed to configure SRAM for IMA rc = %d\n", rc); + goto out; + } + + /* read the start beat count */ + rc = fg_get_beat_count(chip, &start_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + /* write data */ + rc = __fg_interleaved_mem_write(chip, address, offset, val, len); + if (rc < 0) { + if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { + count++; + pr_err("IMA access failed retry_count = %d\n", count); + goto retry; + } + pr_err("failed to write SRAM address rc = %d\n", rc); + goto out; + } + + /* read the end beat count */ + rc = fg_get_beat_count(chip, &end_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + if (atomic_access && start_beat_count != end_beat_count) + pr_err("Start beat_count = %x End beat_count = %x\n", + start_beat_count, end_beat_count); +out: + /* Release IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) + pr_err("failed to reset IMA access bit rc = %d\n", rc); + + return rc; +} + +int fg_ima_init(struct fg_chip *chip) +{ + int rc; + + /* + * Change the FG_MEM_INT interrupt to track IACS_READY + * condition instead of end-of-transaction. This makes sure + * that the next transaction starts only after the hw is ready. + */ + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_INTR_SRC_SLCT_BIT, + IACS_INTR_SRC_SLCT_BIT); + if (rc < 0) { + pr_err("failed to configure interrupt source %d\n", rc); + return rc; + } + + return 0; +} diff --git a/drivers/power/qcom-charger/fg-reg.h b/drivers/power/qcom-charger/fg-reg.h new file mode 100644 index 000000000000..9d5874340a8e --- /dev/null +++ b/drivers/power/qcom-charger/fg-reg.h @@ -0,0 +1,299 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __FG_REG_H__ +#define __FG_REG_H__ + +/* FG_BATT_SOC register definitions */ +#define BATT_SOC_FG_ALG_STS(chip) (chip->batt_soc_base + 0x06) +#define BATT_SOC_FG_ALG_AUX_STS0(chip) (chip->batt_soc_base + 0x07) +#define BATT_SOC_SLEEP_SHUTDOWN_STS(chip) (chip->batt_soc_base + 0x08) +#define BATT_SOC_FG_MONOTONIC_SOC(chip) (chip->batt_soc_base + 0x09) +#define BATT_SOC_FG_MONOTONIC_SOC_CP(chip) (chip->batt_soc_base + 0x0A) +#define BATT_SOC_INT_RT_STS(chip) (chip->batt_soc_base + 0x10) +#define BATT_SOC_EN_CTL(chip) (chip->batt_soc_base + 0x46) +#define BATT_SOC_RESTART(chip) (chip->batt_soc_base + 0x48) +#define BATT_SOC_STS_CLR(chip) (chip->batt_soc_base + 0x4A) +#define BATT_SOC_LOW_PWR_CFG(chip) (chip->batt_soc_base + 0x52) +#define BATT_SOC_LOW_PWR_STS(chip) (chip->batt_soc_base + 0x56) + +/* BATT_SOC_EN_CTL */ +#define FG_ALGORITHM_EN_BIT BIT(7) + +/* BATT_SOC_RESTART */ +#define RESTART_GO_BIT BIT(0) + +/* FG_BATT_INFO register definitions */ +#define BATT_INFO_BATT_TEMP_STS(chip) (chip->batt_info_base + 0x06) +#define BATT_INFO_SYS_BATT(chip) (chip->batt_info_base + 0x07) +#define BATT_INFO_FG_STS(chip) (chip->batt_info_base + 0x09) +#define BATT_INFO_INT_RT_STS(chip) (chip->batt_info_base + 0x10) +#define BATT_INFO_BATT_REM_LATCH(chip) (chip->batt_info_base + 0x4F) +#define BATT_INFO_BATT_TEMP_LSB(chip) (chip->batt_info_base + 0x50) +#define BATT_INFO_BATT_TEMP_MSB(chip) (chip->batt_info_base + 0x51) +#define BATT_INFO_BATT_TEMP_CFG(chip) (chip->batt_info_base + 0x56) +#define BATT_INFO_BATT_TMPR_INTR(chip) (chip->batt_info_base + 0x59) +#define BATT_INFO_THERM_C1(chip) (chip->batt_info_base + 0x5C) +#define BATT_INFO_THERM_C2(chip) (chip->batt_info_base + 0x5D) +#define BATT_INFO_THERM_C3(chip) (chip->batt_info_base + 0x5E) +#define BATT_INFO_THERM_HALF_RANGE(chip) (chip->batt_info_base + 0x5F) +#define BATT_INFO_JEITA_CTLS(chip) (chip->batt_info_base + 0x61) +#define BATT_INFO_JEITA_TOO_COLD(chip) (chip->batt_info_base + 0x62) +#define BATT_INFO_JEITA_COLD(chip) (chip->batt_info_base + 0x63) +#define BATT_INFO_JEITA_HOT(chip) (chip->batt_info_base + 0x64) +#define BATT_INFO_JEITA_TOO_HOT(chip) (chip->batt_info_base + 0x65) + +/* only for v1.1 */ +#define BATT_INFO_ESR_CFG(chip) (chip->batt_info_base + 0x69) +/* starting from v2.0 */ +#define BATT_INFO_ESR_GENERAL_CFG(chip) (chip->batt_info_base + 0x68) +#define BATT_INFO_ESR_PULL_DN_CFG(chip) (chip->batt_info_base + 0x69) +#define BATT_INFO_ESR_FAST_CRG_CFG(chip) (chip->batt_info_base + 0x6A) + +#define BATT_INFO_BATT_MISS_CFG(chip) (chip->batt_info_base + 0x6B) +#define BATT_INFO_WATCHDOG_COUNT(chip) (chip->batt_info_base + 0x70) +#define BATT_INFO_WATCHDOG_CFG(chip) (chip->batt_info_base + 0x71) +#define BATT_INFO_IBATT_SENSING_CFG(chip) (chip->batt_info_base + 0x73) +#define BATT_INFO_QNOVO_CFG(chip) (chip->batt_info_base + 0x74) +#define BATT_INFO_QNOVO_SCALER(chip) (chip->batt_info_base + 0x75) + +/* starting from v2.0 */ +#define BATT_INFO_CRG_SERVICES(chip) (chip->batt_info_base + 0x90) + +/* Following LSB/MSB address are for v2.0 and above; v1.1 have them swapped */ +#define BATT_INFO_VBATT_LSB(chip) (chip->batt_info_base + 0xA0) +#define BATT_INFO_VBATT_MSB(chip) (chip->batt_info_base + 0xA1) +#define BATT_INFO_IBATT_LSB(chip) (chip->batt_info_base + 0xA2) +#define BATT_INFO_IBATT_MSB(chip) (chip->batt_info_base + 0xA3) +#define BATT_INFO_ESR_LSB(chip) (chip->batt_info_base + 0xA4) +#define BATT_INFO_ESR_MSB(chip) (chip->batt_info_base + 0xA5) +#define BATT_INFO_VBATT_LSB_CP(chip) (chip->batt_info_base + 0xA6) +#define BATT_INFO_VBATT_MSB_CP(chip) (chip->batt_info_base + 0xA7) +#define BATT_INFO_IBATT_LSB_CP(chip) (chip->batt_info_base + 0xA8) +#define BATT_INFO_IBATT_MSB_CP(chip) (chip->batt_info_base + 0xA9) +#define BATT_INFO_ESR_LSB_CP(chip) (chip->batt_info_base + 0xAA) +#define BATT_INFO_ESR_MSB_CP(chip) (chip->batt_info_base + 0xAB) +#define BATT_INFO_VADC_LSB(chip) (chip->batt_info_base + 0xAC) +#define BATT_INFO_VADC_MSB(chip) (chip->batt_info_base + 0xAD) +#define BATT_INFO_IADC_LSB(chip) (chip->batt_info_base + 0xAE) +#define BATT_INFO_IADC_MSB(chip) (chip->batt_info_base + 0xAF) +#define BATT_INFO_TM_MISC(chip) (chip->batt_info_base + 0xE5) +#define BATT_INFO_TM_MISC1(chip) (chip->batt_info_base + 0xE6) + +/* BATT_INFO_BATT_TEMP_STS */ +#define JEITA_TOO_HOT_STS_BIT BIT(7) +#define JEITA_HOT_STS_BIT BIT(6) +#define JEITA_COLD_STS_BIT BIT(5) +#define JEITA_TOO_COLD_STS_BIT BIT(4) +#define BATT_TEMP_DELTA_BIT BIT(1) +#define BATT_TEMP_AVAIL_BIT BIT(0) + +/* BATT_INFO_SYS_BATT */ +#define BATT_REM_LATCH_STS_BIT BIT(4) +#define BATT_MISSING_HW_BIT BIT(2) +#define BATT_MISSING_ALG_BIT BIT(1) +#define BATT_MISSING_CMP_BIT BIT(0) + +/* BATT_INFO_FG_STS */ +#define FG_WD_RESET_BIT BIT(7) +/* This bit is not present in v1.1 */ +#define FG_CRG_TRM_BIT BIT(0) + +/* BATT_INFO_INT_RT_STS */ +#define BT_TMPR_DELTA_BIT BIT(6) +#define WDOG_EXP_BIT BIT(5) +#define BT_ATTN_BIT BIT(4) +#define BT_MISS_BIT BIT(3) +#define ESR_DELTA_BIT BIT(2) +#define VBT_LOW_BIT BIT(1) +#define VBT_PRD_DELTA_BIT BIT(0) + +/* BATT_INFO_INT_RT_STS */ +#define BATT_REM_LATCH_CLR_BIT BIT(7) + +/* BATT_INFO_BATT_TEMP_LSB/MSB */ +#define BATT_TEMP_LSB_MASK GENMASK(7, 0) +#define BATT_TEMP_MSB_MASK GENMASK(2, 0) + +/* BATT_INFO_BATT_TEMP_CFG */ +#define JEITA_TEMP_HYST_MASK GENMASK(5, 4) +#define JEITA_TEMP_NO_HYST 0x0 +#define JEITA_TEMP_HYST_1C 0x1 +#define JEITA_TEMP_HYST_2C 0x2 +#define JEITA_TEMP_HYST_3C 0x3 + +/* BATT_INFO_BATT_TMPR_INTR */ +#define CHANGE_THOLD_MASK GENMASK(1, 0) +#define BTEMP_DELTA_2K 0x0 +#define BTEMP_DELTA_4K 0x1 +#define BTEMP_DELTA_6K 0x2 +#define BTEMP_DELTA_10K 0x3 + +/* BATT_INFO_THERM_C1/C2/C3 */ +#define BATT_INFO_THERM_COEFF_MASK GENMASK(7, 0) + +/* BATT_INFO_THERM_HALF_RANGE */ +#define BATT_INFO_THERM_TEMP_MASK GENMASK(7, 0) + +/* BATT_INFO_JEITA_CTLS */ +#define JEITA_STS_CLEAR_BIT BIT(0) + +/* BATT_INFO_JEITA_TOO_COLD/COLD/HOT/TOO_HOT */ +#define JEITA_THOLD_MASK GENMASK(7, 0) + +/* BATT_INFO_ESR_CFG */ +#define CFG_ACTIVE_PD_MASK GENMASK(2, 1) +#define CFG_FCC_DEC_MASK GENMASK(4, 3) + +/* BATT_INFO_ESR_GENERAL_CFG */ +#define ESR_DEEP_TAPER_EN_BIT BIT(0) + +/* BATT_INFO_ESR_PULL_DN_CFG */ +#define ESR_PULL_DOWN_IVAL_MASK GENMASK(3, 2) +#define ESR_MEAS_CUR_60MA 0x0 +#define ESR_MEAS_CUR_120MA 0x1 +#define ESR_MEAS_CUR_180MA 0x2 +#define ESR_MEAS_CUR_240MA 0x3 +#define ESR_PULL_DOWN_MODE_MASK GENMASK(1, 0) +#define ESR_NO_PULL_DOWN 0x0 +#define ESR_STATIC_PULL_DOWN 0x1 +#define ESR_CRG_DSC_PULL_DOWN 0x2 +#define ESR_DSC_PULL_DOWN 0x3 + +/* BATT_INFO_ESR_FAST_CRG_CFG */ +#define ESR_FAST_CRG_IVAL_MASK GENMASK(3, 1) +#define ESR_FCC_300MA 0x0 +#define ESR_FCC_600MA 0x1 +#define ESR_FCC_1A 0x2 +#define ESR_FCC_2A 0x3 +#define ESR_FCC_3A 0x4 +#define ESR_FCC_4A 0x5 +#define ESR_FCC_5A 0x6 +#define ESR_FCC_6A 0x7 +#define ESR_FAST_CRG_CTL_EN_BIT BIT(0) + +/* BATT_INFO_BATT_MISS_CFG */ +#define BM_THERM_TH_MASK GENMASK(5, 4) +#define RES_TH_0P75_MOHM 0x0 +#define RES_TH_1P00_MOHM 0x1 +#define RES_TH_1P50_MOHM 0x2 +#define RES_TH_3P00_MOHM 0x3 +#define BM_BATT_ID_TH_MASK GENMASK(3, 2) +#define BM_FROM_THERM_BIT BIT(1) +#define BM_FROM_BATT_ID_BIT BIT(0) + +/* BATT_INFO_WATCHDOG_COUNT */ +#define WATCHDOG_COUNTER GENMASK(7, 0) + +/* BATT_INFO_WATCHDOG_CFG */ +#define RESET_CAPABLE_BIT BIT(2) +#define PET_CTRL_BIT BIT(1) +#define ENABLE_CTRL_BIT BIT(0) + +/* BATT_INFO_IBATT_SENSING_CFG */ +#define ADC_BITSTREAM_INV_BIT BIT(4) +#define SOURCE_SELECT_MASK GENMASK(1, 0) +#define SRC_SEL_BATFET 0x0 +#define SRC_SEL_RSENSE 0x1 +#define SRC_SEL_BATFET_SMB 0x2 +#define SRC_SEL_RESERVED 0x3 + +/* BATT_INFO_QNOVO_CFG */ +#define LD_REG_FORCE_CTL_BIT BIT(2) +#define LD_REG_CTRL_FORCE_HIGH LD_REG_FORCE_CTL_BIT +#define LD_REG_CTRL_FORCE_LOW 0 +#define LD_REG_CTRL_BIT BIT(1) +#define LD_REG_CTRL_REGISTER LD_REG_CTRL_BIT +#define LD_REG_CTRL_LOGIC 0 +#define BIT_STREAM_CFG_BIT BIT(0) + +/* BATT_INFO_QNOVO_SCALER */ +#define QNOVO_SCALER_MASK GENMASK(7, 0) + +/* BATT_INFO_CRG_SERVICES */ +#define FG_CRC_TRM_EN_BIT BIT(0) + +/* BATT_INFO_VBATT_LSB/MSB */ +#define VBATT_MASK GENMASK(7, 0) + +/* BATT_INFO_IBATT_LSB/MSB */ +#define IBATT_MASK GENMASK(7, 0) + +/* BATT_INFO_ESR_LSB/MSB */ +#define ESR_LSB_MASK GENMASK(7, 0) +#define ESR_MSB_MASK GENMASK(5, 0) + +/* BATT_INFO_VADC_LSB/MSB */ +#define VADC_LSB_MASK GENMASK(7, 0) +#define VADC_MSB_MASK GENMASK(6, 0) + +/* BATT_INFO_IADC_LSB/MSB */ +#define IADC_LSB_MASK GENMASK(7, 0) +#define IADC_MSB_MASK GENMASK(6, 0) + +/* BATT_INFO_TM_MISC */ +#define FORCE_SEQ_RESP_TOGGLE_BIT BIT(6) +#define ALG_DIRECT_VALID_DATA_BIT BIT(5) +#define ALG_DIRECT_MODE_EN_BIT BIT(4) +#define BATT_VADC_CONV_BIT BIT(3) +#define BATT_IADC_CONV_BIT BIT(2) +#define ADC_ENABLE_REG_CTRL_BIT BIT(1) +#define WDOG_FORCE_EXP_BIT BIT(0) +/* only for v1.1 */ +#define ESR_PULSE_FORCE_CTRL_BIT BIT(7) + +/* BATT_INFO_TM_MISC1 */ +/* for v2.0 and above */ +#define ESR_REQ_CTL_BIT BIT(1) +#define ESR_REQ_CTL_EN_BIT BIT(0) + +/* FG_MEM_IF register and bit definitions */ +#define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50) +#define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51) +#define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52) +#define MEM_IF_IMA_OPR_STS(chip) ((chip->mem_if_base) + 0x54) +#define MEM_IF_IMA_EXP_STS(chip) ((chip->mem_if_base) + 0x55) +#define MEM_IF_IMA_HW_STS(chip) ((chip->mem_if_base) + 0x56) +#define MEM_IF_FG_BEAT_COUNT(chip) ((chip->mem_if_base) + 0x57) +#define MEM_IF_IMA_ERR_STS(chip) ((chip->mem_if_base) + 0x5F) +#define MEM_IF_IMA_BYTE_EN(chip) ((chip->mem_if_base) + 0x60) +#define MEM_IF_ADDR_LSB(chip) ((chip->mem_if_base) + 0x61) +#define MEM_IF_ADDR_MSB(chip) ((chip->mem_if_base) + 0x62) +#define MEM_IF_WR_DATA0(chip) ((chip->mem_if_base) + 0x63) +#define MEM_IF_WR_DATA3(chip) ((chip->mem_if_base) + 0x66) +#define MEM_IF_RD_DATA0(chip) ((chip->mem_if_base) + 0x67) +#define MEM_IF_RD_DATA3(chip) ((chip->mem_if_base) + 0x6A) + +/* MEM_IF_MEM_INTF_CFG */ +#define MEM_ACCESS_REQ_BIT BIT(7) +#define IACS_SLCT_BIT BIT(5) + +/* MEM_IF_IMA_CTL */ +#define MEM_ACS_BURST_BIT BIT(7) +#define IMA_WR_EN_BIT BIT(6) +#define IMA_CTL_MASK GENMASK(7, 6) + +/* MEM_IF_IMA_CFG */ +#define IACS_CLR_BIT BIT(2) +#define IACS_INTR_SRC_SLCT_BIT BIT(3) + +/* MEM_IF_IMA_OPR_STS */ +#define IACS_RDY_BIT BIT(1) + +/* MEM_IF_IMA_ERR_STS */ +#define ADDR_STBL_ERR_BIT BIT(7) +#define WR_ACS_ERR_BIT BIT(6) +#define RD_ACS_ERR_BIT BIT(5) + +/* MEM_IF_FG_BEAT_COUNT */ +#define BEAT_COUNT_MASK GENMASK(3, 0) +#endif diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c new file mode 100644 index 000000000000..9f2d9973e04b --- /dev/null +++ b/drivers/power/qcom-charger/fg-util.c @@ -0,0 +1,644 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include "fg-core.h" + +static struct fg_dbgfs dbgfs_data = { + .help_msg = { + .data = + "FG Debug-FS support\n" + "\n" + "Hierarchy schema:\n" + "/sys/kernel/debug/fg_sram\n" + " /help -- Static help text\n" + " /address -- Starting register address for reads or writes\n" + " /count -- Number of registers to read (only used for reads)\n" + " /data -- Initiates the SRAM read (formatted output)\n" + "\n", + }, +}; + +void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) +{ + int pos = 0; + int i; + + for (i = 0; i < buf_len; i++) { + pos += scnprintf(str + pos, str_len - pos, "%02x", buf[i]); + if (i < buf_len - 1) + pos += scnprintf(str + pos, str_len - pos, " "); + } +} + +static inline bool fg_sram_address_valid(u16 address, int len) +{ + if (address > FG_SRAM_ADDRESS_MAX) + return false; + + if ((address + DIV_ROUND_UP(len, 4)) > FG_SRAM_ADDRESS_MAX + 1) + return false; + + return true; +} + +#define SOC_UPDATE_WAIT_MS 1500 +int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags) +{ + int rc = 0; + bool tried_again = false; + bool atomic_access = false; + + if (!chip) + return -ENXIO; + + if (!fg_sram_address_valid(address, len)) + return -EFAULT; + + vote(chip->awake_votable, SRAM_WRITE, true, 0); + mutex_lock(&chip->sram_rw_lock); + + if ((flags & FG_IMA_ATOMIC) && chip->irqs[SOC_UPDATE_IRQ].irq) { + /* + * This interrupt need to be enabled only when it is + * required. It will be kept disabled other times. + */ + enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq); + atomic_access = true; + } else { + flags = FG_IMA_DEFAULT; + } +wait: + /* + * Atomic access mean waiting upon SOC_UPDATE interrupt from + * FG_ALG and do the transaction after that. This is to make + * sure that there will be no SOC update happening when an + * IMA write is happening. SOC_UPDATE interrupt fires every + * FG cycle (~1.47 seconds). + */ + if (atomic_access) { + /* Wait for SOC_UPDATE completion */ + rc = wait_for_completion_interruptible_timeout( + &chip->soc_update, + msecs_to_jiffies(SOC_UPDATE_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_update timed out rc=%d\n", rc); + goto out; + } + } + + rc = fg_interleaved_mem_write(chip, address, offset, val, len, + atomic_access); + if (rc < 0) + pr_err("Error in writing SRAM address 0x%x[%d], rc=%d\n", + address, offset, rc); +out: + if (atomic_access) + disable_irq_nosync(chip->irqs[SOC_UPDATE_IRQ].irq); + + mutex_unlock(&chip->sram_rw_lock); + vote(chip->awake_votable, SRAM_WRITE, false, 0); + return rc; +} + +int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags) +{ + int rc = 0; + + if (!chip) + return -ENXIO; + + if (!fg_sram_address_valid(address, len)) + return -EFAULT; + + vote(chip->awake_votable, SRAM_READ, true, 0); + mutex_lock(&chip->sram_rw_lock); + + rc = fg_interleaved_mem_read(chip, address, offset, val, len); + if (rc < 0) + pr_err("Error in reading SRAM address 0x%x[%d], rc=%d\n", + address, offset, rc); + + mutex_unlock(&chip->sram_rw_lock); + vote(chip->awake_votable, SRAM_READ, false, 0); + return rc; +} + +int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset, + u8 mask, u8 val, int flags) +{ + int rc = 0; + u8 buf[4]; + + rc = fg_sram_read(chip, address, 0, buf, 4, flags); + if (rc < 0) { + pr_err("sram read failed: address=%03X, rc=%d\n", address, rc); + return rc; + } + + buf[offset] &= ~mask; + buf[offset] |= val & mask; + + rc = fg_sram_write(chip, address, 0, buf, 4, flags); + if (rc < 0) { + pr_err("sram write failed: address=%03X, rc=%d\n", address, rc); + return rc; + } + + return rc; +} + +int fg_read(struct fg_chip *chip, int addr, u8 *val, int len) +{ + int rc, i; + + if (!chip || !chip->regmap) + return -ENXIO; + + rc = regmap_bulk_read(chip->regmap, addr, val, len); + + if (rc < 0) { + dev_err(chip->dev, "regmap_read failed for address %04x rc=%d\n", + addr, rc); + return rc; + } + + if (*chip->debug_mask & FG_BUS_READ) { + pr_info("length %d addr=%04x\n", len, addr); + for (i = 0; i < len; i++) + pr_info("val[%d]: %02x\n", i, val[i]); + } + + return 0; +} + +int fg_write(struct fg_chip *chip, int addr, u8 *val, int len) +{ + int rc, i; + bool sec_access = false; + + if (!chip || !chip->regmap) + return -ENXIO; + + mutex_lock(&chip->bus_lock); + sec_access = (addr & 0xFF00) > 0xD0; + if (sec_access) { + rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n", + addr, rc); + goto out; + } + } + + if (len > 1) + rc = regmap_bulk_write(chip->regmap, addr, val, len); + else + rc = regmap_write(chip->regmap, addr, *val); + + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %04x rc=%d\n", + addr, rc); + goto out; + } + + if (*chip->debug_mask & FG_BUS_WRITE) { + pr_info("length %d addr=%04x\n", len, addr); + for (i = 0; i < len; i++) + pr_info("val[%d]: %02x\n", i, val[i]); + } +out: + mutex_unlock(&chip->bus_lock); + return rc; +} + +int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val) +{ + int rc; + bool sec_access = false; + + if (!chip || !chip->regmap) + return -ENXIO; + + mutex_lock(&chip->bus_lock); + sec_access = (addr & 0xFF00) > 0xD0; + if (sec_access) { + rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n", + addr, rc); + goto out; + } + } + + rc = regmap_update_bits(chip->regmap, addr, mask, val); + if (rc < 0) { + dev_err(chip->dev, "regmap_update_bits failed for address %04x rc=%d\n", + addr, rc); + goto out; + } + + fg_dbg(chip, FG_BUS_WRITE, "addr=%04x mask: %02x val: %02x\n", addr, + mask, val); +out: + mutex_unlock(&chip->bus_lock); + return rc; +} + +int64_t twos_compliment_extend(int64_t val, int sign_bit_pos) +{ + int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8); + int64_t mask, val_out; + + val_out = val; + mask = 1 << sign_bit_pos; + if (val & mask) { + for (i = 8; i > nbytes; i--) { + mask = 0xFFLL << ((i - 1) * 8); + val_out |= mask; + } + + if ((nbytes * 8) - 1 > sign_bit_pos) { + mask = 1 << sign_bit_pos; + for (i = 1; i <= (nbytes * 8) - sign_bit_pos; i++) + val_out |= mask << i; + } + } + + pr_debug("nbytes: %d val: %llx val_out: %llx\n", nbytes, val, val_out); + return val_out; +} + +/* All the debugfs related functions are defined below */ +static int fg_sram_dfs_open(struct inode *inode, struct file *file) +{ + struct fg_log_buffer *log; + struct fg_trans *trans; + u8 *data_buf; + + size_t logbufsize = SZ_4K; + size_t databufsize = SZ_4K; + + if (!dbgfs_data.chip) { + pr_err("Not initialized data\n"); + return -EINVAL; + } + + /* Per file "transaction" data */ + trans = devm_kzalloc(dbgfs_data.chip->dev, sizeof(*trans), GFP_KERNEL); + if (!trans) + return -ENOMEM; + + /* Allocate log buffer */ + log = devm_kzalloc(dbgfs_data.chip->dev, logbufsize, GFP_KERNEL); + if (!log) + return -ENOMEM; + + log->rpos = 0; + log->wpos = 0; + log->len = logbufsize - sizeof(*log); + + /* Allocate data buffer */ + data_buf = devm_kzalloc(dbgfs_data.chip->dev, databufsize, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + trans->log = log; + trans->data = data_buf; + trans->cnt = dbgfs_data.cnt; + trans->addr = dbgfs_data.addr; + trans->chip = dbgfs_data.chip; + trans->offset = trans->addr; + + file->private_data = trans; + return 0; +} + +static int fg_sram_dfs_close(struct inode *inode, struct file *file) +{ + struct fg_trans *trans = file->private_data; + + if (trans && trans->log && trans->data) { + file->private_data = NULL; + devm_kfree(trans->chip->dev, trans->log); + devm_kfree(trans->chip->dev, trans->data); + devm_kfree(trans->chip->dev, trans); + } + + return 0; +} + +/** + * print_to_log: format a string and place into the log buffer + * @log: The log buffer to place the result into. + * @fmt: The format string to use. + * @...: The arguments for the format string. + * + * The return value is the number of characters written to @log buffer + * not including the trailing '\0'. + */ +static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...) +{ + va_list args; + int cnt; + char *buf = &log->data[log->wpos]; + size_t size = log->len - log->wpos; + + va_start(args, fmt); + cnt = vscnprintf(buf, size, fmt, args); + va_end(args); + + log->wpos += cnt; + return cnt; +} + +/** + * write_next_line_to_log: Writes a single "line" of data into the log buffer + * @trans: Pointer to SRAM transaction data. + * @offset: SRAM address offset to start reading from. + * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read. + * + * The 'offset' is a 12-bit SRAM address. + * + * On a successful read, the pcnt is decremented by the number of data + * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have + * been read. + */ +static int write_next_line_to_log(struct fg_trans *trans, int offset, + size_t *pcnt) +{ + int i; + u8 data[ITEMS_PER_LINE]; + u16 address; + struct fg_log_buffer *log = trans->log; + int cnt = 0; + int items_to_read = min(ARRAY_SIZE(data), *pcnt); + int items_to_log = min(ITEMS_PER_LINE, items_to_read); + + /* Buffer needs enough space for an entire line */ + if ((log->len - log->wpos) < MAX_LINE_LENGTH) + goto done; + + memcpy(data, trans->data + (offset - trans->addr), items_to_read); + *pcnt -= items_to_read; + + /* address is in word now and it increments by 1. */ + address = trans->addr + ((offset - trans->addr) / ITEMS_PER_LINE); + cnt = print_to_log(log, "%3.3d ", address & 0xfff); + if (cnt == 0) + goto done; + + /* Log the data items */ + for (i = 0; i < items_to_log; ++i) { + cnt = print_to_log(log, "%2.2X ", data[i]); + if (cnt == 0) + goto done; + } + + /* If the last character was a space, then replace it with a newline */ + if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') + log->data[log->wpos - 1] = '\n'; + +done: + return cnt; +} + +/** + * get_log_data - reads data from SRAM and saves to the log buffer + * @trans: Pointer to SRAM transaction data. + * + * Returns the number of "items" read or SPMI error code for read failures. + */ +static int get_log_data(struct fg_trans *trans) +{ + int cnt, rc; + int last_cnt; + int items_read; + int total_items_read = 0; + u32 offset = trans->offset; + size_t item_cnt = trans->cnt; + struct fg_log_buffer *log = trans->log; + + if (item_cnt == 0) + return 0; + + if (item_cnt > SZ_4K) { + pr_err("Reading too many bytes\n"); + return -EINVAL; + } + + pr_debug("addr: %d offset: %d count: %d\n", trans->addr, trans->offset, + trans->cnt); + rc = fg_sram_read(trans->chip, trans->addr, 0, + trans->data, trans->cnt, 0); + if (rc < 0) { + pr_err("SRAM read failed: rc = %d\n", rc); + return rc; + } + /* Reset the log buffer 'pointers' */ + log->wpos = log->rpos = 0; + + /* Keep reading data until the log is full */ + do { + last_cnt = item_cnt; + cnt = write_next_line_to_log(trans, offset, &item_cnt); + items_read = last_cnt - item_cnt; + offset += items_read; + total_items_read += items_read; + } while (cnt && item_cnt > 0); + + /* Adjust the transaction offset and count */ + trans->cnt = item_cnt; + trans->offset += total_items_read; + + return total_items_read; +} + +/** + * fg_sram_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a + * byte array (coded as string) + * @file: file pointer + * @buf: where to put the result + * @count: maximum space available in @buf + * @ppos: starting position + * @return number of user bytes read, or negative error value + */ +static ssize_t fg_sram_dfs_reg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct fg_trans *trans = file->private_data; + struct fg_log_buffer *log = trans->log; + size_t ret; + size_t len; + + /* Is the the log buffer empty */ + if (log->rpos >= log->wpos) { + if (get_log_data(trans) <= 0) + return 0; + } + + len = min(count, log->wpos - log->rpos); + + ret = copy_to_user(buf, &log->data[log->rpos], len); + if (ret == len) { + pr_err("error copy sram register values to user\n"); + return -EFAULT; + } + + /* 'ret' is the number of bytes not copied */ + len -= ret; + + *ppos += len; + log->rpos += len; + return len; +} + +/** + * fg_sram_dfs_reg_write: write user's byte array (coded as string) to SRAM. + * @file: file pointer + * @buf: user data to be written. + * @count: maximum space available in @buf + * @ppos: starting position + * @return number of user byte written, or negative error value + */ +static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int bytes_read; + int data; + int pos = 0; + int cnt = 0; + u8 *values; + char *kbuf; + size_t ret = 0; + struct fg_trans *trans = file->private_data; + u32 address = trans->addr; + + /* Make a copy of the user data */ + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = copy_from_user(kbuf, buf, count); + if (ret == count) { + pr_err("failed to copy data from user\n"); + ret = -EFAULT; + goto free_buf; + } + + count -= ret; + *ppos += count; + kbuf[count] = '\0'; + + /* Override the text buffer with the raw data */ + values = kbuf; + + /* Parse the data in the buffer. It should be a string of numbers */ + while ((pos < count) && + sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) { + pos += bytes_read; + values[cnt++] = data & 0xff; + } + + if (!cnt) + goto free_buf; + + pr_debug("address %d, count %d\n", address, cnt); + /* Perform the write(s) */ + + ret = fg_sram_write(trans->chip, address, 0, values, cnt, 0); + if (ret) { + pr_err("SRAM write failed, err = %zu\n", ret); + } else { + ret = count; + trans->offset += cnt > 4 ? 4 : cnt; + } + +free_buf: + kfree(kbuf); + return ret; +} + +static const struct file_operations fg_sram_dfs_reg_fops = { + .open = fg_sram_dfs_open, + .release = fg_sram_dfs_close, + .read = fg_sram_dfs_reg_read, + .write = fg_sram_dfs_reg_write, +}; + +/* + * fg_debugfs_create: adds new fg_sram debugfs entry + * @return zero on success + */ +int fg_sram_debugfs_create(struct fg_chip *chip) +{ + struct dentry *root; + struct dentry *file; + mode_t dfs_mode = S_IRUSR | S_IWUSR; + + pr_debug("Creating FG_SRAM debugfs file-system\n"); + root = debugfs_create_dir("fg_sram", NULL); + if (IS_ERR_OR_NULL(root)) { + pr_err("Error creating top level directory err:%ld", + (long)root); + if (PTR_ERR(root) == -ENODEV) + pr_err("debugfs is not enabled in the kernel"); + return -ENODEV; + } + + if (!root) + return -ENOENT; + + dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data); + file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg); + if (!file) { + pr_err("error creating help entry\n"); + goto err_remove_fs; + } + + dbgfs_data.chip = chip; + + file = debugfs_create_u32("count", dfs_mode, root, &(dbgfs_data.cnt)); + if (!file) { + pr_err("error creating 'count' entry\n"); + goto err_remove_fs; + } + + file = debugfs_create_x32("address", dfs_mode, + root, &(dbgfs_data.addr)); + if (!file) { + pr_err("error creating 'address' entry\n"); + goto err_remove_fs; + } + + file = debugfs_create_file("data", dfs_mode, root, &dbgfs_data, + &fg_sram_dfs_reg_fops); + if (!file) { + pr_err("error creating 'data' entry\n"); + goto err_remove_fs; + } + + chip->dentry = root; + return 0; + +err_remove_fs: + debugfs_remove_recursive(root); + return -ENOMEM; +} diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c new file mode 100644 index 000000000000..2adc07ddc5a0 --- /dev/null +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -0,0 +1,1495 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_batterydata.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/iio/consumer.h> +#include <linux/qpnp/qpnp-revid.h> +#include "fg-core.h" +#include "fg-reg.h" + +#define FG_GEN3_DEV_NAME "qcom,fg-gen3" + +#define PERPH_SUBTYPE_REG 0x05 +#define FG_BATT_SOC_PMICOBALT 0x10 +#define FG_BATT_INFO_PMICOBALT 0x11 +#define FG_MEM_INFO_PMICOBALT 0x0D + +/* SRAM address and offset in ascending order */ +#define CUTOFF_VOLT_WORD 5 +#define CUTOFF_VOLT_OFFSET 0 +#define SYS_TERM_CURR_WORD 6 +#define SYS_TERM_CURR_OFFSET 0 +#define DELTA_SOC_THR_WORD 12 +#define DELTA_SOC_THR_OFFSET 3 +#define RECHARGE_SOC_THR_WORD 14 +#define RECHARGE_SOC_THR_OFFSET 0 +#define CHG_TERM_CURR_WORD 14 +#define CHG_TERM_CURR_OFFSET 1 +#define EMPTY_VOLT_WORD 15 +#define EMPTY_VOLT_OFFSET 0 +#define VBATT_LOW_WORD 15 +#define VBATT_LOW_OFFSET 1 +#define PROFILE_LOAD_WORD 24 +#define PROFILE_LOAD_OFFSET 0 +#define NOM_CAP_WORD 58 +#define NOM_CAP_OFFSET 0 +#define PROFILE_INTEGRITY_WORD 79 +#define PROFILE_INTEGRITY_OFFSET 3 +#define BATT_SOC_WORD 91 +#define BATT_SOC_OFFSET 0 +#define MONOTONIC_SOC_WORD 94 +#define MONOTONIC_SOC_OFFSET 2 +#define VOLTAGE_PRED_WORD 97 +#define VOLTAGE_PRED_OFFSET 0 +#define OCV_WORD 97 +#define OCV_OFFSET 2 +#define RSLOW_WORD 101 +#define RSLOW_OFFSET 0 +#define LAST_BATT_SOC_WORD 119 +#define LAST_BATT_SOC_OFFSET 0 +#define LAST_MONOTONIC_SOC_WORD 119 +#define LAST_MONOTONIC_SOC_OFFSET 2 + +static int fg_decode_value_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val); +static int fg_decode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val); +static void fg_encode_voltage_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_voltage_8b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_current(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); + +#define PARAM(_id, _addr, _offset, _len, _num, _den, _enc, _dec) \ + [FG_SRAM_##_id] = { \ + .address = _addr, \ + .offset = _offset, \ + .len = _len, \ + .numrtr = _num, \ + .denmtr = _den, \ + .encode = _enc, \ + .decode = _dec, \ + } \ + +static struct fg_sram_param pmicobalt_v1_sram_params[] = { + PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, NULL, + fg_decode_default), + PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, + 1000, NULL, fg_decode_value_16b), + PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, NULL, + fg_decode_value_16b), + PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 244141, 1000, NULL, + fg_decode_value_16b), + /* Entries below here are configurable during initialization */ + PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000, + 244141, fg_encode_voltage_16b, NULL), + PARAM(EMPTY_VOLT, EMPTY_VOLT_WORD, EMPTY_VOLT_OFFSET, 1, 100000, 390625, + fg_encode_voltage_8b, NULL), + PARAM(VBATT_LOW, VBATT_LOW_WORD, VBATT_LOW_OFFSET, 1, 100000, 390625, + fg_encode_voltage_8b, NULL), + PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3, + 1000000, 122070, fg_encode_current, NULL), + PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1, + 100000, 390625, fg_encode_current, NULL), + PARAM(DELTA_SOC_THR, DELTA_SOC_THR_WORD, DELTA_SOC_THR_OFFSET, 1, 256, + 100, fg_encode_default, NULL), + PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET, + 1, 256, 100, fg_encode_default, NULL), +}; + +static int fg_gen3_debug_mask; +module_param_named( + debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR +); + +static int fg_sram_update_period_ms = 30000; +module_param_named( + sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR +); + +/* Other functions HERE */ + +static int fg_awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct fg_chip *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + pr_debug("client: %s awake: %d\n", client, awake); + return 0; +} + +static bool is_charger_available(struct fg_chip *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +static void status_change_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, status_change_work); + union power_supply_propval prop = {0, }; + + if (!is_charger_available(chip)) { + fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); + return; + } + + power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, + &prop); + switch (prop.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); + break; + case POWER_SUPPLY_STATUS_FULL: + fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); + break; + default: + break; + } +} + +#define PROFILE_LEN 224 +#define PROFILE_COMP_LEN 32 +#define SOC_READY_WAIT_MS 2000 +static void profile_load_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, + profile_load_work.work); + int rc; + u8 buf[PROFILE_COMP_LEN], val; + bool tried_again = false, profiles_same = false; + + if (!chip->batt_id_avail) { + pr_err("batt_id not available\n"); + return; + } + + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to read profile integrity rc=%d\n", rc); + return; + } + + vote(chip->awake_votable, PROFILE_LOAD, true, 0); + if (val == 0x01) { + fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); + rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading battery profile, rc:%d\n", rc); + goto out; + } + profiles_same = memcmp(chip->batt_profile, buf, + PROFILE_COMP_LEN) == 0; + if (profiles_same) { + fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); + goto done; + } + fg_dbg(chip, FG_STATUS, "profiles are different?\n"); + } + + fg_dbg(chip, FG_STATUS, "profile loading started\n"); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + + /* load battery profile */ + rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("Error in writing battery profile, rc:%d\n", rc); + goto out; + } + + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, + RESTART_GO_BIT); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + +wait: + rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, + msecs_to_jiffies(SOC_READY_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_ready timed out rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "SOC is ready\n"); + + /* Set the profile integrity bit */ + val = 0x1; + rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to write profile integrity rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "profile loaded successfully"); +done: + rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, + NOM_CAP_OFFSET, rc); + goto out; + } + + chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; + chip->profile_loaded = true; +out: + vote(chip->awake_votable, PROFILE_LOAD, false, 0); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); +} + +/* All getters HERE */ + +static int fg_decode_value_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int value) +{ + sp[id].value = div_u64((u64)(u16)value * sp[id].numrtr, sp[id].denmtr); + pr_debug("id: %d raw value: %x decoded value: %x\n", id, value, + sp[id].value); + return sp[id].value; +} + +static int fg_decode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int value) +{ + return value; +} + +static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, + int value) +{ + if (!sp[id].decode) { + pr_err("No decoding function for parameter %d\n", id); + return -EINVAL; + } + + return sp[id].decode(sp, id, value); +} + +static void fg_encode_voltage_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_voltage_8b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + /* Offset is 2.5V */ + val -= 2500; + temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_current(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + s64 current_ma; + + current_ma = -val; + temp = (int64_t)div_s64(current_ma * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val, u8 *buf) +{ + if (!sp[id].encode) { + pr_err("No encoding function for parameter %d\n", id); + return; + } + + sp[id].encode(sp, id, val, buf); +} + +/* + * Please make sure *_sram_params table has the entry for the parameter + * obtained through this function. In addition to address, offset, + * length from where this SRAM parameter is read, a decode function + * need to be specified. + */ +static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id, + int *val) +{ + int temp, rc, i; + u8 buf[4]; + + if (id < 0 || id > FG_SRAM_MAX || chip->sp[id].len > sizeof(buf)) + return -EINVAL; + + vote(chip->awake_votable, SRAM_UPDATE, true, 0); + rc = fg_sram_read(chip, chip->sp[id].address, chip->sp[id].offset, + buf, chip->sp[id].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error reading address 0x%04x[%d] rc=%d\n", + chip->sp[id].address, chip->sp[id].offset, rc); + goto out; + } + + for (i = 0, temp = 0; i < chip->sp[id].len; i++) + temp |= buf[i] << (8 * i); + + *val = fg_decode(chip->sp, id, temp); + return 0; +out: + vote(chip->awake_votable, SRAM_UPDATE, false, 0); + return rc; +} + +#define BATT_TEMP_NUMR 1 +#define BATT_TEMP_DENR 1 +static int fg_get_battery_temp(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_BATT_TEMP_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_BATT_TEMP_LSB(chip), rc); + return rc; + } + + temp = ((buf[1] & BATT_TEMP_MSB_MASK) << 8) | + (buf[0] & BATT_TEMP_LSB_MASK); + temp = DIV_ROUND_CLOSEST(temp, 4); + + /* Value is in Kelvin; Convert it to deciDegC */ + temp = (temp - 273) * 10; + *val = temp; + return 0; +} + +#define BATT_ESR_NUMR 244141 +#define BATT_ESR_DENR 1000 +static int fg_get_battery_esr(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_ESR_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = ((buf[0] & ESR_MSB_MASK) << 8) | + (buf[1] & ESR_LSB_MASK); + else + temp = ((buf[1] & ESR_MSB_MASK) << 8) | + (buf[0] & ESR_LSB_MASK); + + pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp); + *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR); + return 0; +} + +static int fg_get_battery_resistance(struct fg_chip *chip, int *val) +{ + int rc, esr, rslow; + + rc = fg_get_battery_esr(chip, &esr); + if (rc < 0) { + pr_err("failed to get ESR, rc=%d\n", rc); + return rc; + } + + rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &rslow); + if (rc < 0) { + pr_err("failed to get Rslow, rc=%d\n", rc); + return rc; + } + + *val = esr + rslow; + return 0; +} + +#define BATT_CURRENT_NUMR 488281 +#define BATT_CURRENT_DENR 1000 +static int fg_get_battery_current(struct fg_chip *chip, int *val) +{ + int rc = 0; + int64_t temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_IBATT_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_IBATT_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = buf[0] << 8 | buf[1]; + else + temp = buf[1] << 8 | buf[0]; + + pr_debug("buf: %x %x temp: %llx\n", buf[0], buf[1], temp); + /* Sign bit is bit 15 */ + temp = twos_compliment_extend(temp, 15); + *val = div_s64((s64)temp * BATT_CURRENT_NUMR, BATT_CURRENT_DENR); + return 0; +} + +#define BATT_VOLTAGE_NUMR 122070 +#define BATT_VOLTAGE_DENR 1000 +static int fg_get_battery_voltage(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_VBATT_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_VBATT_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = buf[0] << 8 | buf[1]; + else + temp = buf[1] << 8 | buf[0]; + + pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp); + *val = div_u64((u64)temp * BATT_VOLTAGE_NUMR, BATT_VOLTAGE_DENR); + return 0; +} + +#define MAX_TRIES_SOC 5 +static int fg_get_msoc_raw(struct fg_chip *chip, int *val) +{ + u8 cap[2]; + int rc, tries = 0; + + while (tries < MAX_TRIES_SOC) { + rc = fg_read(chip, BATT_SOC_FG_MONOTONIC_SOC(chip), cap, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_SOC_FG_MONOTONIC_SOC(chip), rc); + return rc; + } + + if (cap[0] == cap[1]) + break; + + tries++; + } + + if (tries == MAX_TRIES_SOC) { + pr_err("shadow registers do not match\n"); + return -EINVAL; + } + + fg_dbg(chip, FG_POWER_SUPPLY, "raw: 0x%02x\n", cap[0]); + + *val = cap[0]; + return 0; +} + +#define FULL_CAPACITY 100 +#define FULL_SOC_RAW 255 +static int fg_get_prop_capacity(struct fg_chip *chip, int *val) +{ + int rc, msoc; + + rc = fg_get_msoc_raw(chip, &msoc); + if (rc < 0) + return rc; + + *val = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW); + return 0; +} + +#define DEFAULT_BATT_TYPE "Unknown Battery" +#define MISSING_BATT_TYPE "Missing Battery" +#define LOADING_BATT_TYPE "Loading Battery" +static const char *fg_get_battery_type(struct fg_chip *chip) +{ + if (chip->battery_missing) + return MISSING_BATT_TYPE; + + if (chip->bp.batt_type_str) { + if (chip->profile_loaded) + return chip->bp.batt_type_str; + else + return LOADING_BATT_TYPE; + } + + return DEFAULT_BATT_TYPE; +} + +static int fg_get_batt_id(struct fg_chip *chip, int *val) +{ + int rc, batt_id = -EINVAL; + + if (!chip->batt_id_chan) + return -EINVAL; + + rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id); + if (rc < 0) { + pr_err("Error in reading batt_id channel, rc:%d\n", rc); + return rc; + } + + chip->batt_id_avail = true; + fg_dbg(chip, FG_STATUS, "batt_id: %d\n", batt_id); + + *val = batt_id; + return 0; +} + +static int fg_get_batt_profile(struct fg_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *batt_node, *profile_node; + const char *data; + int rc, len, batt_id; + + rc = fg_get_batt_id(chip, &batt_id); + if (rc < 0) { + pr_err("Error in getting batt_id rc:%d\n", rc); + return rc; + } + + batt_node = of_find_node_by_name(node, "qcom,battery-data"); + if (!batt_node) { + pr_err("Batterydata not available\n"); + return -ENXIO; + } + + batt_id /= 1000; + profile_node = of_batterydata_get_best_profile(batt_node, batt_id, + NULL); + if (IS_ERR(profile_node)) + return PTR_ERR(profile_node); + + if (!profile_node) { + pr_err("couldn't find profile handle\n"); + return -ENODATA; + } + + rc = of_property_read_string(profile_node, "qcom,battery-type", + &chip->bp.batt_type_str); + if (rc < 0) { + pr_err("battery type unavailable, rc:%d\n", rc); + return rc; + } + + rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", + &chip->bp.float_volt_uv); + if (rc < 0) { + pr_err("battery float voltage unavailable, rc:%d\n", rc); + chip->bp.float_volt_uv = -EINVAL; + } + + rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah", + &chip->bp.fastchg_curr_ma); + if (rc < 0) { + pr_err("battery nominal capacity unavailable, rc:%d\n", rc); + chip->bp.fastchg_curr_ma = -EINVAL; + } + + data = of_get_property(profile_node, "qcom,fg-profile-data", &len); + if (!data) { + pr_err("No profile data available\n"); + return -ENODATA; + } + + if (len != PROFILE_LEN) { + pr_err("battery profile incorrect size: %d\n", len); + return -EINVAL; + } + + memcpy(chip->batt_profile, data, len); + return 0; +} + +static inline void get_temp_setpoint(int threshold, u8 *val) +{ + /* Resolution is 0.5C. Base is -30C. */ + *val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5); +} + +/* PSY CALLBACKS STAY HERE */ + +static int fg_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *pval) +{ + struct fg_chip *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + rc = fg_get_prop_capacity(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = fg_get_battery_voltage(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = fg_get_battery_current(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_TEMP: + rc = fg_get_battery_temp(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_RESISTANCE: + rc = fg_get_battery_resistance(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + rc = fg_get_sram_prop(chip, FG_SRAM_OCV, &pval->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + pval->intval = chip->nom_cap_uah; + break; + case POWER_SUPPLY_PROP_RESISTANCE_ID: + rc = fg_get_batt_id(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_BATTERY_TYPE: + pval->strval = fg_get_battery_type(chip); + break; + default: + break; + } + + return rc; +} + +static int fg_psy_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *pval) +{ + switch (psp) { + default: + break; + } + + return 0; +} + +static int fg_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + default: + break; + } + + return 0; +} + +static void fg_external_power_changed(struct power_supply *psy) +{ + pr_debug("power supply changed\n"); +} + +static int fg_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + struct fg_chip *chip = container_of(nb, struct fg_chip, nb); + + if (event != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if ((strcmp(psy->desc->name, "battery") == 0) + || (strcmp(psy->desc->name, "usb") == 0)) + schedule_work(&chip->status_change_work); + + return NOTIFY_OK; +} + +static enum power_supply_property fg_psy_props[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_RESISTANCE_ID, + POWER_SUPPLY_PROP_RESISTANCE, + POWER_SUPPLY_PROP_BATTERY_TYPE, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +}; + +static const struct power_supply_desc fg_psy_desc = { + .name = "bms", + .type = POWER_SUPPLY_TYPE_BMS, + .properties = fg_psy_props, + .num_properties = ARRAY_SIZE(fg_psy_props), + .get_property = fg_psy_get_property, + .set_property = fg_psy_set_property, + .external_power_changed = fg_external_power_changed, + .property_is_writeable = fg_property_is_writeable, +}; + +/* INIT FUNCTIONS STAY HERE */ + +static int fg_hw_init(struct fg_chip *chip) +{ + int rc; + u8 buf[4], val; + + fg_encode(chip->sp, FG_SRAM_CUTOFF_VOLT, chip->dt.cutoff_volt_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CUTOFF_VOLT].address, + chip->sp[FG_SRAM_CUTOFF_VOLT].offset, buf, + chip->sp[FG_SRAM_CUTOFF_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing cutoff_volt, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_EMPTY_VOLT, chip->dt.empty_volt_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_EMPTY_VOLT].address, + chip->sp[FG_SRAM_EMPTY_VOLT].offset, buf, + chip->sp[FG_SRAM_EMPTY_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing empty_volt, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_CHG_TERM_CURR, chip->dt.chg_term_curr_ma, + buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CHG_TERM_CURR].address, + chip->sp[FG_SRAM_CHG_TERM_CURR].offset, buf, + chip->sp[FG_SRAM_CHG_TERM_CURR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing chg_term_curr, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_SYS_TERM_CURR, chip->dt.sys_term_curr_ma, + buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_SYS_TERM_CURR].address, + chip->sp[FG_SRAM_SYS_TERM_CURR].offset, buf, + chip->sp[FG_SRAM_SYS_TERM_CURR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing sys_term_curr, rc=%d\n", rc); + return rc; + } + + if (chip->dt.vbatt_low_thr_mv > 0) { + fg_encode(chip->sp, FG_SRAM_VBATT_LOW, + chip->dt.vbatt_low_thr_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_VBATT_LOW].address, + chip->sp[FG_SRAM_VBATT_LOW].offset, buf, + chip->sp[FG_SRAM_VBATT_LOW].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing vbatt_low_thr, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.delta_soc_thr > 0 && chip->dt.delta_soc_thr < 100) { + fg_encode(chip->sp, FG_SRAM_DELTA_SOC_THR, + chip->dt.delta_soc_thr, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_DELTA_SOC_THR].address, + chip->sp[FG_SRAM_DELTA_SOC_THR].offset, + buf, chip->sp[FG_SRAM_DELTA_SOC_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing delta_soc_thr, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) { + fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, + chip->dt.recharge_soc_thr, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].address, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].offset, + buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing recharge_soc_thr, rc=%d\n", + rc); + return rc; + } + } + + if (chip->dt.rsense_sel >= SRC_SEL_BATFET && + chip->dt.rsense_sel < SRC_SEL_RESERVED) { + rc = fg_masked_write(chip, BATT_INFO_IBATT_SENSING_CFG(chip), + SOURCE_SELECT_MASK, chip->dt.rsense_sel); + if (rc < 0) { + pr_err("Error in writing rsense_sel, rc=%d\n", rc); + return rc; + } + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COLD], &val); + rc = fg_write(chip, BATT_INFO_JEITA_TOO_COLD(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_cold, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COOL], &val); + rc = fg_write(chip, BATT_INFO_JEITA_COLD(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_cool, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_WARM], &val); + rc = fg_write(chip, BATT_INFO_JEITA_HOT(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_warm, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_HOT], &val); + rc = fg_write(chip, BATT_INFO_JEITA_TOO_HOT(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_hot, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int fg_memif_init(struct fg_chip *chip) +{ + return fg_ima_init(chip); +} + +static int fg_batt_profile_init(struct fg_chip *chip) +{ + int rc; + + if (!chip->batt_profile) { + chip->batt_profile = devm_kcalloc(chip->dev, PROFILE_LEN, + sizeof(*chip->batt_profile), + GFP_KERNEL); + if (!chip->batt_profile) + return -ENOMEM; + } + + rc = fg_get_batt_profile(chip); + if (rc < 0) { + pr_err("Error in getting battery profile, rc:%d\n", rc); + return rc; + } + + schedule_delayed_work(&chip->profile_load_work, msecs_to_jiffies(0)); + return 0; +} + +/* INTERRUPT HANDLERS STAY HERE */ + +static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + u8 status; + int rc; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + rc = fg_read(chip, BATT_INFO_INT_RT_STS(chip), &status, 1); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_INT_RT_STS(chip), rc); + return IRQ_HANDLED; + } + + chip->battery_missing = (status & BT_MISS_BIT); + + if (chip->battery_missing) { + chip->batt_id_avail = false; + chip->profile_loaded = false; + } else { + rc = fg_batt_profile_init(chip); + if (rc < 0) { + pr_err("Error in initializing battery profile, rc=%d\n", + rc); + return IRQ_HANDLED; + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_first_est_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + complete_all(&chip->soc_ready); + return IRQ_HANDLED; +} + +static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + complete_all(&chip->soc_update); + return IRQ_HANDLED; +} + +static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_dummy_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { + /* BATT_SOC irqs */ + [MSOC_FULL_IRQ] = { + "msoc-full", fg_soc_irq_handler, true }, + [MSOC_HIGH_IRQ] = { + "msoc-high", fg_soc_irq_handler, true }, + [MSOC_EMPTY_IRQ] = { + "msoc-empty", fg_empty_soc_irq_handler, true }, + [MSOC_LOW_IRQ] = { + "msoc-low", fg_soc_irq_handler }, + [MSOC_DELTA_IRQ] = { + "msoc-delta", fg_delta_soc_irq_handler, true }, + [BSOC_DELTA_IRQ] = { + "bsoc-delta", fg_delta_soc_irq_handler, true }, + [SOC_READY_IRQ] = { + "soc-ready", fg_first_est_irq_handler, true }, + [SOC_UPDATE_IRQ] = { + "soc-update", fg_soc_update_irq_handler }, + /* BATT_INFO irqs */ + [BATT_TEMP_DELTA_IRQ] = { + "batt-temp-delta", fg_delta_batt_temp_irq_handler }, + [BATT_MISSING_IRQ] = { + "batt-missing", fg_batt_missing_irq_handler, true }, + [ESR_DELTA_IRQ] = { + "esr-delta", fg_dummy_irq_handler }, + [VBATT_LOW_IRQ] = { + "vbatt-low", fg_vbatt_low_irq_handler, true }, + [VBATT_PRED_DELTA_IRQ] = { + "vbatt-pred-delta", fg_dummy_irq_handler }, + /* MEM_IF irqs */ + [DMA_GRANT_IRQ] = { + "dma-grant", fg_dummy_irq_handler }, + [MEM_XCP_IRQ] = { + "mem-xcp", fg_dummy_irq_handler }, + [IMA_RDY_IRQ] = { + "ima-rdy", fg_dummy_irq_handler }, +}; + +static int fg_get_irq_index_byname(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fg_irqs); i++) { + if (strcmp(fg_irqs[i].name, name) == 0) + return i; + } + + pr_err("%s is not in irq list\n", name); + return -ENOENT; +} + +static int fg_register_interrupts(struct fg_chip *chip) +{ + struct device_node *child, *node = chip->dev->of_node; + struct property *prop; + const char *name; + int rc, irq, irq_index; + + for_each_available_child_of_node(node, child) { + of_property_for_each_string(child, "interrupt-names", prop, + name) { + irq = of_irq_get_byname(child, name); + if (irq < 0) { + dev_err(chip->dev, "failed to get irq %s irq:%d\n", + name, irq); + return irq; + } + + irq_index = fg_get_irq_index_byname(name); + if (irq_index < 0) + return irq_index; + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + fg_irqs[irq_index].handler, + IRQF_ONESHOT, name, chip); + if (rc < 0) { + dev_err(chip->dev, "failed to register irq handler for %s rc:%d\n", + name, rc); + return rc; + } + + fg_irqs[irq_index].irq = irq; + if (fg_irqs[irq_index].wakeable) + enable_irq_wake(fg_irqs[irq_index].irq); + } + } + + return 0; +} + +#define DEFAULT_CUTOFF_VOLT_MV 3200 +#define DEFAULT_EMPTY_VOLT_MV 3100 +#define DEFAULT_CHG_TERM_CURR_MA 100 +#define DEFAULT_SYS_TERM_CURR_MA 125 +#define DEFAULT_DELTA_SOC_THR 1 +#define DEFAULT_RECHARGE_SOC_THR 95 +#define DEFAULT_BATT_TEMP_COLD 0 +#define DEFAULT_BATT_TEMP_COOL 5 +#define DEFAULT_BATT_TEMP_WARM 45 +#define DEFAULT_BATT_TEMP_HOT 50 +static int fg_parse_dt(struct fg_chip *chip) +{ + struct device_node *child, *revid_node, *node = chip->dev->of_node; + u32 base, temp; + u8 subtype; + int rc, len; + + if (!node) { + dev_err(chip->dev, "device tree node missing\n"); + return -ENXIO; + } + + revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); + if (!revid_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + + chip->pmic_rev_id = get_revid_data(revid_node); + if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(chip->pmic_rev_id)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + pr_debug("PMIC subtype %d Digital major %d\n", + chip->pmic_rev_id->pmic_subtype, chip->pmic_rev_id->rev4); + + switch (chip->pmic_rev_id->pmic_subtype) { + case PMICOBALT_SUBTYPE: + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + chip->sp = pmicobalt_v1_sram_params; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + chip->batt_id_chan = iio_channel_get(chip->dev, "rradc_batt_id"); + if (IS_ERR(chip->batt_id_chan)) { + if (PTR_ERR(chip->batt_id_chan) != -EPROBE_DEFER) + pr_err("batt_id_chan unavailable %ld\n", + PTR_ERR(chip->batt_id_chan)); + rc = PTR_ERR(chip->batt_id_chan); + chip->batt_id_chan = NULL; + return rc; + } + + if (of_get_available_child_count(node) == 0) { + dev_err(chip->dev, "No child nodes specified!\n"); + return -ENXIO; + } + + for_each_available_child_of_node(node, child) { + rc = of_property_read_u32(child, "reg", &base); + if (rc < 0) { + dev_err(chip->dev, "reg not specified in node %s, rc=%d\n", + child->full_name, rc); + return rc; + } + + rc = fg_read(chip, base + PERPH_SUBTYPE_REG, &subtype, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read subtype for base %d, rc=%d\n", + base, rc); + return rc; + } + + switch (subtype) { + case FG_BATT_SOC_PMICOBALT: + chip->batt_soc_base = base; + break; + case FG_BATT_INFO_PMICOBALT: + chip->batt_info_base = base; + break; + case FG_MEM_INFO_PMICOBALT: + chip->mem_if_base = base; + break; + default: + dev_err(chip->dev, "Invalid peripheral subtype 0x%x\n", + subtype); + return -ENXIO; + } + } + + /* Read all the optional properties below */ + rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage", &temp); + if (rc < 0) + chip->dt.cutoff_volt_mv = DEFAULT_CUTOFF_VOLT_MV; + else + chip->dt.cutoff_volt_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-empty-voltage", &temp); + if (rc < 0) + chip->dt.empty_volt_mv = DEFAULT_EMPTY_VOLT_MV; + else + chip->dt.empty_volt_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-vbatt-low-thr", &temp); + if (rc < 0) + chip->dt.vbatt_low_thr_mv = -EINVAL; + else + chip->dt.vbatt_low_thr_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-chg-term-current", &temp); + if (rc < 0) + chip->dt.chg_term_curr_ma = DEFAULT_CHG_TERM_CURR_MA; + else + chip->dt.chg_term_curr_ma = temp; + + rc = of_property_read_u32(node, "qcom,fg-sys-term-current", &temp); + if (rc < 0) + chip->dt.sys_term_curr_ma = DEFAULT_SYS_TERM_CURR_MA; + else + chip->dt.sys_term_curr_ma = temp; + + rc = of_property_read_u32(node, "qcom,fg-delta-soc-thr", &temp); + if (rc < 0) + chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR; + else + chip->dt.delta_soc_thr = temp; + + rc = of_property_read_u32(node, "qcom,fg-recharge-soc-thr", &temp); + if (rc < 0) + chip->dt.recharge_soc_thr = DEFAULT_RECHARGE_SOC_THR; + else + chip->dt.recharge_soc_thr = temp; + + rc = of_property_read_u32(node, "qcom,fg-rsense-sel", &temp); + if (rc < 0) + chip->dt.rsense_sel = SRC_SEL_BATFET_SMB; + else + chip->dt.rsense_sel = (u8)temp & SOURCE_SELECT_MASK; + + chip->dt.jeita_thresholds[JEITA_COLD] = DEFAULT_BATT_TEMP_COLD; + chip->dt.jeita_thresholds[JEITA_COOL] = DEFAULT_BATT_TEMP_COOL; + chip->dt.jeita_thresholds[JEITA_WARM] = DEFAULT_BATT_TEMP_WARM; + chip->dt.jeita_thresholds[JEITA_HOT] = DEFAULT_BATT_TEMP_HOT; + if (of_find_property(node, "qcom,fg-jeita-thresholds", &len)) { + if (len == NUM_JEITA_LEVELS) { + rc = of_property_read_u32_array(node, + "qcom,fg-jeita-thresholds", + chip->dt.jeita_thresholds, len); + if (rc < 0) + pr_warn("Error reading Jeita thresholds, default values will be used rc:%d\n", + rc); + } + } + + return 0; +} + +static void fg_cleanup(struct fg_chip *chip) +{ + power_supply_unreg_notifier(&chip->nb); + debugfs_remove_recursive(chip->dentry); + if (chip->awake_votable) + destroy_votable(chip->awake_votable); + + if (chip->batt_id_chan) + iio_channel_release(chip->batt_id_chan); + + dev_set_drvdata(chip->dev, NULL); +} + +static int fg_gen3_probe(struct platform_device *pdev) +{ + struct fg_chip *chip; + struct power_supply_config fg_psy_cfg; + int rc; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + chip->debug_mask = &fg_gen3_debug_mask; + chip->irqs = fg_irqs; + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + dev_err(chip->dev, "Parent regmap is unavailable\n"); + return -ENXIO; + } + + rc = fg_parse_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in reading DT parameters, rc:%d\n", + rc); + return rc; + } + + chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb, + chip); + if (IS_ERR(chip->awake_votable)) { + rc = PTR_ERR(chip->awake_votable); + return rc; + } + + mutex_init(&chip->bus_lock); + mutex_init(&chip->sram_rw_lock); + init_completion(&chip->soc_update); + init_completion(&chip->soc_ready); + INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); + INIT_WORK(&chip->status_change_work, status_change_work); + + rc = fg_memif_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n", + rc); + goto exit; + } + + rc = fg_hw_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n", + rc); + goto exit; + } + + platform_set_drvdata(pdev, chip); + + /* Register the power supply */ + fg_psy_cfg.drv_data = chip; + fg_psy_cfg.of_node = NULL; + fg_psy_cfg.supplied_to = NULL; + fg_psy_cfg.num_supplicants = 0; + chip->fg_psy = devm_power_supply_register(chip->dev, &fg_psy_desc, + &fg_psy_cfg); + if (IS_ERR(chip->fg_psy)) { + pr_err("failed to register fg_psy rc = %ld\n", + PTR_ERR(chip->fg_psy)); + goto exit; + } + + chip->nb.notifier_call = fg_notifier_cb; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + goto exit; + } + + rc = fg_register_interrupts(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in registering interrupts, rc:%d\n", + rc); + goto exit; + } + + /* Keep SOC_UPDATE irq disabled until we require it */ + if (fg_irqs[SOC_UPDATE_IRQ].irq) + disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq); + + rc = fg_sram_debugfs_create(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n", + rc); + goto exit; + } + + rc = fg_batt_profile_init(chip); + if (rc < 0) + dev_warn(chip->dev, "Error in initializing battery profile, rc:%d\n", + rc); + + device_init_wakeup(chip->dev, true); + pr_debug("FG GEN3 driver successfully probed\n"); + return 0; +exit: + fg_cleanup(chip); + return rc; +} + +static int fg_gen3_remove(struct platform_device *pdev) +{ + struct fg_chip *chip = dev_get_drvdata(&pdev->dev); + + fg_cleanup(chip); + return 0; +} + +static const struct of_device_id fg_gen3_match_table[] = { + {.compatible = FG_GEN3_DEV_NAME}, + {}, +}; + +static struct platform_driver fg_gen3_driver = { + .driver = { + .name = FG_GEN3_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = fg_gen3_match_table, + }, + .probe = fg_gen3_probe, + .remove = fg_gen3_remove, +}; + +static int __init fg_gen3_init(void) +{ + return platform_driver_register(&fg_gen3_driver); +} + +static void __exit fg_gen3_exit(void) +{ + return platform_driver_unregister(&fg_gen3_driver); +} + +module_init(fg_gen3_init); +module_exit(fg_gen3_exit); + +MODULE_DESCRIPTION("QPNP Fuel gauge GEN3 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" FG_GEN3_DEV_NAME); diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index ad00a987ae41..541e40aeb91a 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -108,6 +108,13 @@ static struct smb_params v1_params = { .max_u = 3000000, .step_u = 25000, }, + .jeita_cc_comp = { + .name = "jeita fcc reduction", + .reg = JEITA_CCCOMP_CFG_REG, + .min_u = 0, + .max_u = 1575000, + .step_u = 25000, + }, }; struct smb_dt_props { @@ -722,49 +729,49 @@ struct smb2_irq_info { static struct smb2_irq_info smb2_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_chg_state_change, true }, - { "step-chg-state-change", smblib_handle_debug }, - { "step-chg-soc-update-fail", smblib_handle_debug }, + { "chg-error", smblib_handle_debug }, + { "chg-state-change", smblib_handle_chg_state_change, true }, + { "step-chg-state-change", smblib_handle_debug }, + { "step-chg-soc-update-fail", smblib_handle_debug }, { "step-chg-soc-update-request", smblib_handle_debug }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { "otg-fail", smblib_handle_debug }, + { "otg-overcurrent", smblib_handle_debug }, + { "otg-oc-dis-sw-sts", smblib_handle_debug }, + { "testmode-change-detect", smblib_handle_debug }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_psy_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { "bat-temp", smblib_handle_batt_temp_changed }, + { "bat-ocp", smblib_handle_batt_psy_changed }, + { "bat-ov", smblib_handle_batt_psy_changed }, + { "bat-low", smblib_handle_batt_psy_changed }, + { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, + { "bat-terminal-missing", smblib_handle_batt_psy_changed }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin, true }, - { "usbin-src-change", smblib_handle_usb_source_change, true }, - { "usbin-icl-change", smblib_handle_icl_change, true }, + { "usbin-collapse", smblib_handle_debug }, + { "usbin-lt-3p6v", smblib_handle_debug }, + { "usbin-uv", smblib_handle_debug }, + { "usbin-ov", smblib_handle_debug }, + { "usbin-plugin", smblib_handle_usb_plugin, true }, + { "usbin-src-change", smblib_handle_usb_source_change, true }, + { "usbin-icl-change", smblib_handle_icl_change, true }, { "type-c-change", smblib_handle_usb_typec_change, true }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { "dcin-collapse", smblib_handle_debug }, + { "dcin-lt-3p6v", smblib_handle_debug }, + { "dcin-uv", smblib_handle_debug }, + { "dcin-ov", smblib_handle_debug }, + { "dcin-plugin", smblib_handle_debug }, + { "div2-en-dg", smblib_handle_debug }, + { "dcin-icl-change", smblib_handle_debug }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", NULL }, - { "wdog-bark", NULL }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_debug }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { "wdog-snarl", NULL }, + { "wdog-bark", NULL }, + { "aicl-fail", smblib_handle_debug }, + { "aicl-done", smblib_handle_debug }, + { "high-duty-cycle", smblib_handle_debug }, + { "input-current-limiting", smblib_handle_debug }, + { "temperature-change", smblib_handle_debug }, + { "switcher-power-ok", smblib_handle_debug }, }; static int smb2_get_irq_index_byname(const char *irq_name) diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 55bcc9ec443e..dd3ec1eb51e3 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -30,7 +30,7 @@ static bool is_secure(struct smb_charger *chg, int addr) { /* assume everything above 0xC0 is secure */ - return (bool)(addr >= 0xC0); + return (bool)((addr & 0xFF) >= 0xC0); } int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) @@ -85,10 +85,36 @@ unlock: static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc, int *master_ua, int *slave_ua) { + int rc, cc_reduction_ua = 0; int master_percent = min(max(*chg->pl.master_percent, 0), 100); + union power_supply_propval pval = {0, }; + + /* + * if master_percent is 0, s/w will configure master's fcc to zero and + * slave's fcc to the max. However since master's fcc is zero it + * disables its own charging and as a result the slave's charging is + * disabled via the fault line. + */ + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc == 0) { + if (pval.intval == POWER_SUPPLY_HEALTH_WARM + || pval.intval == POWER_SUPPLY_HEALTH_COOL) { + rc = smblib_get_charge_param(chg, + &chg->param.jeita_cc_comp, + &cc_reduction_ua); + if (rc < 0) { + dev_err(chg->dev, "Could not get jeita comp, rc=%d\n", + rc); + cc_reduction_ua = 0; + } + } + } + + total_fcc = max(0, total_fcc - cc_reduction_ua); *master_ua = (total_fcc * master_percent) / 100; *slave_ua = (total_fcc - *master_ua) * chg->pl.taper_percent / 100; + *master_ua += cc_reduction_ua; } /******************** @@ -846,6 +872,7 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } + power_supply_changed(chg->batt_psy); return rc; } @@ -1253,6 +1280,16 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) return IRQ_HANDLED; } +irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + rerun_election(chg->fcc_votable); + power_supply_changed(chg->batt_psy); + return IRQ_HANDLED; +} + irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -1280,15 +1317,6 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) int rc; u8 stat; - rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); - if (rc < 0) { - dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", - rc); - return IRQ_HANDLED; - } - - chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); - /* fetch the DPDM regulator */ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, "dpdm-supply", NULL)) { @@ -1303,18 +1331,30 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) if (!chg->dpdm_reg) goto skip_dpdm_float; - if (chg->vbus_present && !regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); - rc = regulator_enable(chg->dpdm_reg); - if (rc < 0) - dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n", - rc); - } else if (regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); - rc = regulator_disable(chg->dpdm_reg); - if (rc < 0) - dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n", - rc); + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return IRQ_HANDLED; + } + + chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + + if (chg->vbus_present) { + if (!regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); + rc = regulator_enable(chg->dpdm_reg); + if (rc < 0) + dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n", + rc); + } + } else { + if (regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); + rc = regulator_disable(chg->dpdm_reg); + if (rc < 0) + dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n", + rc); + } } skip_dpdm_float: diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 1ee4945b625f..2e35e1e3b174 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -68,6 +68,7 @@ struct smb_params { struct smb_chg_param dc_icl_div2_mid_lv; struct smb_chg_param dc_icl_div2_mid_hv; struct smb_chg_param dc_icl_div2_hv; + struct smb_chg_param jeita_cc_comp; }; struct parallel_params { @@ -150,6 +151,7 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t smblib_handle_debug(int irq, void *data); irqreturn_t smblib_handle_chg_state_change(int irq, void *data); +irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data); irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data); irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data); irqreturn_t smblib_handle_usb_plugin(int irq, void *data); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3e167f4c0f42..4d406c51d884 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4092,8 +4092,9 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_EXIT); dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d", __func__, ret); + ret = ufshcd_link_recovery(hba); /* Unable to recover the link, so no point proceeding */ - if (ufshcd_link_recovery(hba)) + if (ret) BUG(); } else { dev_dbg(hba->dev, "%s: Hibern8 Exit at %lld us", __func__, diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c index 2769d08b3056..98fce5e5c06a 100644 --- a/drivers/slimbus/slim-msm-ngd.c +++ b/drivers/slimbus/slim-msm-ngd.c @@ -711,7 +711,6 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc, struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl); int i, ret; struct msm_slim_endp *endpoint = &dev->tx_msgq; - struct sps_pipe *pipe = endpoint->sps; u32 *header; DECLARE_COMPLETION_ONSTACK(done); @@ -808,8 +807,8 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc, goto retpath; } - ret = sps_transfer_one(pipe, dev->bulk.wr_dma, dev->bulk.size, NULL, - SPS_IOVEC_FLAG_EOT); + ret = sps_transfer_one(endpoint->sps, dev->bulk.wr_dma, dev->bulk.size, + NULL, SPS_IOVEC_FLAG_EOT); if (ret) { SLIM_WARN(dev, "sps transfer one returned error:%d", ret); goto retpath; @@ -1471,11 +1470,13 @@ static void ngd_adsp_down(struct msm_slim_ctrl *dev) struct slim_controller *ctrl = &dev->ctrl; struct slim_device *sbdev; + mutex_lock(&dev->ssr_lock); ngd_slim_enable(dev, false); /* device up should be called again after SSR */ list_for_each_entry(sbdev, &ctrl->devs, dev_list) slim_report_absent(sbdev); SLIM_INFO(dev, "SLIM ADSP SSR (DOWN) done\n"); + mutex_unlock(&dev->ssr_lock); } static void ngd_adsp_up(struct work_struct *work) @@ -1484,7 +1485,9 @@ static void ngd_adsp_up(struct work_struct *work) container_of(work, struct msm_slim_qmi, ssr_up); struct msm_slim_ctrl *dev = container_of(qmi, struct msm_slim_ctrl, qmi); + mutex_lock(&dev->ssr_lock); ngd_slim_enable(dev, true); + mutex_unlock(&dev->ssr_lock); } static ssize_t show_mask(struct device *device, struct device_attribute *attr, @@ -1648,6 +1651,7 @@ static int ngd_slim_probe(struct platform_device *pdev) init_completion(&dev->reconf); init_completion(&dev->ctrl_up); mutex_init(&dev->tx_lock); + mutex_init(&dev->ssr_lock); spin_lock_init(&dev->tx_buf_lock); spin_lock_init(&dev->rx_lock); dev->ee = 1; diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h index 86d2606182fa..7859d1e79e39 100644 --- a/drivers/slimbus/slim-msm.h +++ b/drivers/slimbus/slim-msm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -284,6 +284,7 @@ struct msm_slim_ctrl { struct clk *rclk; struct clk *hclk; struct mutex tx_lock; + struct mutex ssr_lock; spinlock_t tx_buf_lock; u8 pgdla; enum msm_slim_msgq use_rx_msgqs; diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 36b9a9435910..adbf2dc7a166 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -83,7 +83,9 @@ obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o obj-$(CONFIG_MSM_AVTIMER) += avtimer.o +ifdef CONFIG_ARCH_MSM8996 obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o +endif obj-$(CONFIG_MSM_JTAGV8) += jtag-fuse.o jtagv8.o jtagv8-etm.o obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 883f23d8234d..ea25ed5d0611 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -2387,7 +2387,7 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&penv->event_lock); spin_lock_init(&penv->on_off_lock); - penv->event_wq = alloc_workqueue("icnss_driver_event", 0, 0); + penv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); if (!penv->event_wq) { icnss_pr_err("Workqueue creation failed\n"); ret = -EFAULT; @@ -2487,11 +2487,11 @@ static int icnss_suspend(struct platform_device *pdev, icnss_pr_dbg("Driver suspending, state: 0x%lx\n", penv->state); - if (!penv->ops) + if (!penv->ops || !penv->ops->suspend || + !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; - if (penv->ops->suspend) - ret = penv->ops->suspend(&pdev->dev, state); + ret = penv->ops->suspend(&pdev->dev, state); out: if (ret == 0) @@ -2511,11 +2511,11 @@ static int icnss_resume(struct platform_device *pdev) icnss_pr_dbg("Driver resuming, state: 0x%lx\n", penv->state); - if (!penv->ops) + if (!penv->ops || !penv->ops->resume || + !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; - if (penv->ops->resume) - ret = penv->ops->resume(&pdev->dev); + ret = penv->ops->resume(&pdev->dev); out: if (ret == 0) diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index e4d235957981..2bc425a437b2 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -48,6 +48,7 @@ module_param_named(enable, locator_status, uint, S_IRUGO | S_IWUSR); static void service_locator_svc_arrive(struct work_struct *work); static void service_locator_svc_exit(struct work_struct *work); static void service_locator_recv_msg(struct work_struct *work); +static void pd_locator_work(struct work_struct *work); struct workqueue_struct *servloc_wq; @@ -61,6 +62,11 @@ struct pd_qmi_data { struct qmi_handle *clnt_handle; }; +struct pd_qmi_work { + struct work_struct pd_loc_work; + struct pd_qmi_client_data *pdc; + struct notifier_block *notifier; +}; DEFINE_MUTEX(service_init_mutex); struct pd_qmi_data service_locator; @@ -288,7 +294,6 @@ out: static int init_service_locator(void) { - static bool service_inited; int rc = 0; mutex_lock(&service_init_mutex); @@ -324,50 +329,86 @@ static int init_service_locator(void) goto inited; } - rc = wait_for_completion_timeout(&service_locator.service_available, - msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT)); - if (!rc) { - rc = -ENODEV; - mutex_unlock(&service_init_mutex); - pr_err("Process domain service locator response timeout!\n"); - goto error; - } + wait_for_completion(&service_locator.service_available); service_inited = true; mutex_unlock(&service_init_mutex); pr_info("Service locator initialized\n"); return 0; -error: - qmi_svc_event_notifier_unregister(SERVREG_LOC_SERVICE_ID_V01, - SERVREG_LOC_SERVICE_VERS_V01, SERVREG_LOC_SERVICE_INSTANCE_ID, - &service_locator.notifier); - destroy_workqueue(servloc_wq); + inited: mutex_unlock(&service_init_mutex); return rc; } -int get_service_location(struct pd_qmi_client_data *data) +int get_service_location(char *client_name, char *service_name, + struct notifier_block *locator_nb) { + struct pd_qmi_client_data *pqcd; + struct pd_qmi_work *pqw; int rc = 0; - if (!data || !data->client_name || !data->service_name) { + if (!locator_nb || !client_name || !service_name) { rc = -EINVAL; pr_err("Invalid input!\n"); goto err; } + + pqcd = kmalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL); + if (!pqcd) { + rc = -ENOMEM; + pr_err("Allocation failed\n"); + goto err; + } + strlcpy(pqcd->client_name, client_name, ARRAY_SIZE(pqcd->client_name)); + strlcpy(pqcd->service_name, service_name, + ARRAY_SIZE(pqcd->service_name)); + + pqw = kmalloc(sizeof(struct pd_qmi_work), GFP_KERNEL); + if (!pqw) { + rc = -ENOMEM; + pr_err("Allocation failed\n"); + goto err; + } + pqw->notifier = locator_nb; + pqw->pdc = pqcd; + + INIT_WORK(&pqw->pd_loc_work, pd_locator_work); + schedule_work(&pqw->pd_loc_work); + +err: + return rc; +} +EXPORT_SYMBOL(get_service_location); + +static void pd_locator_work(struct work_struct *work) +{ + int rc = 0; + struct pd_qmi_client_data *data; + struct pd_qmi_work *pdqw = container_of(work, struct pd_qmi_work, + pd_loc_work); + + data = pdqw->pdc; rc = init_service_locator(); if (rc) { pr_err("Unable to connect to service locator!, rc = %d\n", rc); + pdqw->notifier->notifier_call(pdqw->notifier, + LOCATOR_DOWN, NULL); goto err; } rc = service_locator_send_msg(data); - if (rc) + if (rc) { pr_err("Failed to get process domains for %s for client %s\n", data->service_name, data->client_name); + pdqw->notifier->notifier_call(pdqw->notifier, + LOCATOR_DOWN, NULL); + goto err; + } + pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data); + err: - return rc; + kfree(data); + kfree(pdqw); } -EXPORT_SYMBOL(get_service_location); int find_subsys(const char *pd_path, char *subsys) { @@ -391,75 +432,137 @@ EXPORT_SYMBOL(find_subsys); static struct pd_qmi_client_data test_data; -static ssize_t show_servloc(struct seq_file *f, void *unused) +static int servloc_test_pdr_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) { - int rc = 0, i = 0; + int i, rc = 0; char subsys[QMI_SERVREG_LOC_NAME_LENGTH_V01]; + struct pd_qmi_client_data *return_data; - rc = get_service_location(&test_data); - if (rc) { - seq_printf(f, "Failed to get process domain!, rc = %d\n", rc); + return_data = (struct pd_qmi_client_data *)ptr; + + if (opcode) { + pr_err("%s: Failed to get process domain!, opcode = %lu\n", + __func__, opcode); return -EIO; } - seq_printf(f, "Service Name: %s\tTotal Domains: %d\n", - test_data.service_name, test_data.total_domains); - for (i = 0; i < test_data.total_domains; i++) { - seq_printf(f, "Instance ID: %d\t ", - test_data.domain_list[i].instance_id); - seq_printf(f, "Domain Name: %s\n", - test_data.domain_list[i].name); - rc = find_subsys(test_data.domain_list[i].name, subsys); + pr_err("Service Name: %s\tTotal Domains: %d\n", + return_data->service_name, return_data->total_domains); + + for (i = 0; i < return_data->total_domains; i++) { + pr_err("Instance ID: %d\t ", + return_data->domain_list[i].instance_id); + pr_err("Domain Name: %s\n", + return_data->domain_list[i].name); + rc = find_subsys(return_data->domain_list[i].name, + subsys); if (rc < 0) - seq_printf(f, "No valid subsys found for %s!\n", - test_data.domain_list[i].name); + pr_err("No valid subsys found for %s!\n", + return_data->domain_list[i].name); else - seq_printf(f, "Subsys: %s\n", subsys); - } + pr_err("Subsys: %s\n", subsys); + } return 0; } -static ssize_t store_servloc(struct file *fp, const char __user *buf, - size_t count, loff_t *unused) +static struct notifier_block pdr_service_nb = { + .notifier_call = servloc_test_pdr_cb, +}; + +static ssize_t servloc_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) { + int rc = 0; + char *node_name = filp->private_data; + + if (!strcmp(node_name, "test_servloc_get")) + rc = get_service_location(test_data.client_name, + test_data.service_name, &pdr_service_nb); + + return rc; +} + +static ssize_t servloc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *unused) +{ + char *node_name = fp->private_data; + if (!buf) return -EIO; - snprintf(test_data.service_name, sizeof(test_data.service_name), + if (!strcmp(node_name, "service_name")) { + snprintf(test_data.service_name, sizeof(test_data.service_name), "%.*s", (int) min((size_t)count - 1, (sizeof(test_data.service_name) - 1)), buf); + } else { + snprintf(test_data.client_name, sizeof(test_data.client_name), + "%.*s", (int) min((size_t)count - 1, + (sizeof(test_data.client_name) - 1)), buf); + } return count; } -static int servloc_open(struct inode *inode, struct file *file) -{ - return single_open(file, (void *)show_servloc, inode->i_private); -} - static const struct file_operations servloc_fops = { - .open = servloc_open, - .read = seq_read, - .write = store_servloc, - .llseek = seq_lseek, - .release = seq_release, + .open = simple_open, + .read = servloc_read, + .write = servloc_write, }; +static struct dentry *servloc_base_dir; static struct dentry *test_servloc_file; +static int __init servloc_debugfs_init(void) +{ + servloc_base_dir = debugfs_create_dir("test_servloc", NULL); + return !servloc_base_dir ? -ENOMEM : 0; +} + +static void servloc_debugfs_exit(void) +{ + debugfs_remove_recursive(servloc_base_dir); +} + +static int servloc_debugfs_add(void) +{ + int rc; + + if (!servloc_base_dir) + return -ENOMEM; + + test_servloc_file = debugfs_create_file("client_name", + S_IRUGO | S_IWUSR, servloc_base_dir, + "client_name", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + + if (rc == 0) { + test_servloc_file = debugfs_create_file("service_name", + S_IRUGO | S_IWUSR, servloc_base_dir, + "service_name", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + } + + if (rc == 0) { + test_servloc_file = debugfs_create_file("test_servloc_get", + S_IRUGO | S_IWUSR, servloc_base_dir, + "test_servloc_get", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + } + return rc; +} + static int __init service_locator_init(void) { pr_debug("service_locator_status = %d\n", locator_status); - test_servloc_file = debugfs_create_file("test_servloc", - S_IRUGO | S_IWUSR, NULL, NULL, - &servloc_fops); - if (!test_servloc_file) - pr_err("Could not create test_servloc debugfs entry!"); + if (servloc_debugfs_init()) + pr_err("Could not create test_servloc base directory!"); + if (servloc_debugfs_add()) + pr_err("Could not create test_servloc node entries!"); return 0; } static void __exit service_locator_exit(void) { - debugfs_remove(test_servloc_file); + servloc_debugfs_exit(); } - module_init(service_locator_init); module_exit(service_locator_exit); diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index 9e2ba25ce1ac..73d7435d2eb8 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -759,9 +759,9 @@ enum tsens_trip_type { }; enum tsens_tm_trip_type { - TSENS_TM_TRIP_CRITICAL = 0, - TSENS_TM_TRIP_WARM, + TSENS_TM_TRIP_WARM = 0, TSENS_TM_TRIP_COOL, + TSENS_TM_TRIP_CRITICAL, TSENS_TM_TRIP_NUM, }; @@ -1575,9 +1575,6 @@ static int tsens_tm_get_trip_type(struct thermal_zone_device *thermal, case TSENS_TM_TRIP_COOL: *type = THERMAL_TRIP_CONFIGURABLE_LOW; break; - case TSENS_TM_TRIP_CRITICAL: - *type = THERMAL_TRIP_CRITICAL; - break; default: return -EINVAL; } @@ -5717,8 +5714,8 @@ static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev) tmdev->sensor[i].tm = tmdev; if (tmdev->tsens_type == TSENS_TYPE3) { tmdev->sensor[i].tz_dev = thermal_zone_device_register( - name, TSENS_TM_TRIP_NUM, - TSENS_TM_WRITABLE_TRIPS_MASK, + name, TSENS_TRIP_NUM, + TSENS_WRITABLE_TRIPS_MASK, &tmdev->sensor[i], &tsens_tm_thermal_zone_ops, NULL, 0, 0); if (IS_ERR(tmdev->sensor[i].tz_dev)) { diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index bbc6a8e96d41..ff40d6fad922 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -3615,7 +3615,7 @@ static int hotplug_init_cpu_offlined(void) int temp = 0; uint32_t cpu = 0; - if (!hotplug_enabled) + if (!hotplug_enabled || !hotplug_task) return 0; mutex_lock(&core_control_mutex); @@ -3632,8 +3632,7 @@ static int hotplug_init_cpu_offlined(void) if (temp >= msm_thermal_info.hotplug_temp_degC) cpus[cpu].offline = 1; - else if (temp <= (msm_thermal_info.hotplug_temp_degC - - msm_thermal_info.hotplug_temp_hysteresis_degC)) + else cpus[cpu].offline = 0; } mutex_unlock(&core_control_mutex); diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c index 500d47d4ffdf..84ab45fde4ae 100644 --- a/drivers/thermal/qpnp-adc-tm.c +++ b/drivers/thermal/qpnp-adc-tm.c @@ -250,6 +250,8 @@ struct qpnp_adc_tm_sensor { bool thermal_node; uint32_t scale_type; struct list_head thr_list; + bool high_thr_triggered; + bool low_thr_triggered; }; struct qpnp_adc_tm_chip { @@ -501,8 +503,8 @@ static int32_t qpnp_adc_tm_rc_check_channel_en(struct qpnp_adc_tm_chip *chip) } adc_tm_ctl &= QPNP_BTM_Mn_MEAS_EN; - status_low &= QPNP_BTM_Mn_LOW_THR_INT_EN; - status_high &= QPNP_BTM_Mn_HIGH_THR_INT_EN; + status_low = adc_tm_ctl & QPNP_BTM_Mn_LOW_THR_INT_EN; + status_high = adc_tm_ctl & QPNP_BTM_Mn_HIGH_THR_INT_EN; /* Enable only if there are pending measurement requests */ if ((adc_tm_ctl && status_high) || @@ -1497,11 +1499,13 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num)) return -EINVAL; + mutex_lock(&chip->adc->adc_lock); + btm_chan = adc_tm->btm_channel_num; rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx); if (rc < 0) { pr_err("Invalid btm channel idx\n"); - return rc; + goto fail; } if (mode == THERMAL_DEVICE_ENABLED) { @@ -1527,14 +1531,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop); if (rc) { pr_err("adc-tm configure failed with %d\n", rc); - return -EINVAL; + goto fail; } } else { rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop); if (rc) { pr_err("hc configure failed with %d\n", rc); - return -EINVAL; + goto fail; } } } else if (mode == THERMAL_DEVICE_DISABLED) { @@ -1545,7 +1549,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_mode_select(chip, mode_ctl); if (rc < 0) { pr_err("adc-tm single mode select failed\n"); - return rc; + goto fail; } } @@ -1553,7 +1557,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_disable(chip); if (rc < 0) { pr_err("adc-tm disable failed\n"); - return rc; + goto fail; } if (!chip->adc_tm_hc) { @@ -1561,14 +1565,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_req_sts_check(chip); if (rc < 0) { pr_err("adc-tm req_sts check failed\n"); - return rc; + goto fail; } rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, false); if (rc < 0) { pr_err("multi measurement update failed\n"); - return rc; + goto fail; } } else { rc = qpnp_adc_tm_reg_update(chip, @@ -1576,19 +1580,22 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, QPNP_BTM_Mn_MEAS_EN, false); if (rc < 0) { pr_err("multi measurement disable failed\n"); - return rc; + goto fail; } } rc = qpnp_adc_tm_enable_if_channel_meas(chip); if (rc < 0) { pr_err("re-enabling measurement failed\n"); - return rc; + goto fail; } } adc_tm->mode = mode; +fail: + mutex_unlock(&chip->adc->adc_lock); + return 0; } @@ -2096,17 +2103,207 @@ fail: return rc; } -static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) +static int qpnp_adc_tm_disable_rearm_high_thresholds( + struct qpnp_adc_tm_chip *chip, int sensor_num) { - u8 sensor_mask = 0, notify_check = 0; - int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0; + + struct qpnp_adc_thr_client_info *client_info = NULL; + struct list_head *thr_list; uint32_t btm_chan_num = 0; + u8 sensor_mask = 0, notify_check = 0; + int rc = 0; + + btm_chan_num = chip->sensor[sensor_num].btm_channel_num; + pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", + sensor_num, chip->th_info.adc_tm_high_enable, + chip->th_info.adc_tm_low_enable, + chip->th_info.qpnp_adc_tm_meas_en); + if (!chip->sensor[sensor_num].thermal_node) { + /* + * For non thermal registered clients such as usb_id, + * vbatt, pmic_therm + */ + sensor_mask = 1 << sensor_num; + pr_debug("non thermal node - mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_recalib_request_check(chip, + sensor_num, true, ¬ify_check); + if (rc < 0 || !notify_check) { + pr_debug("Calib recheck re-armed rc=%d\n", rc); + chip->th_info.adc_tm_high_enable = 0; + return rc; + } + } else { + /* + * Uses the thermal sysfs registered device to disable + * the corresponding high voltage threshold which + * is triggered by low temp + */ + sensor_mask = 1 << sensor_num; + pr_debug("thermal node with mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_activate_trip_type( + chip->sensor[sensor_num].tz_dev, + ADC_TM_TRIP_LOW_COOL, + THERMAL_TRIP_ACTIVATION_DISABLED); + if (rc < 0) { + pr_err("notify error:%d\n", sensor_num); + return rc; + } + } + list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { + client_info = list_entry(thr_list, + struct qpnp_adc_thr_client_info, list); + if (client_info->high_thr_set) { + client_info->high_thr_set = false; + client_info->notify_high_thr = true; + if (client_info->state_req_copy == + ADC_TM_HIGH_LOW_THR_ENABLE) + client_info->state_req_copy = + ADC_TM_LOW_THR_ENABLE; + else + client_info->state_req_copy = + ADC_TM_HIGH_THR_DISABLE; + } + } + qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); + + if (!chip->adc_tm_hc) { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_MULTI_MEAS_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } else { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_BTM_Mn_EN(sensor_num), + QPNP_BTM_Mn_MEAS_EN, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } + + rc = qpnp_adc_tm_enable_if_channel_meas(chip); + if (rc < 0) { + pr_err("re-enabling measurement failed\n"); + return rc; + } + + queue_work(chip->sensor[sensor_num].req_wq, + &chip->sensor[sensor_num].work); + + return rc; +} + +static int qpnp_adc_tm_disable_rearm_low_thresholds( + struct qpnp_adc_tm_chip *chip, int sensor_num) +{ struct qpnp_adc_thr_client_info *client_info = NULL; struct list_head *thr_list; + uint32_t btm_chan_num = 0; + u8 sensor_mask = 0, notify_check = 0; + int rc = 0; + + btm_chan_num = chip->sensor[sensor_num].btm_channel_num; + pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", + sensor_num, chip->th_info.adc_tm_high_enable, + chip->th_info.adc_tm_low_enable, + chip->th_info.qpnp_adc_tm_meas_en); + if (!chip->sensor[sensor_num].thermal_node) { + /* + * For non thermal registered clients such as usb_id, + * vbatt, pmic_therm + */ + pr_debug("non thermal node - mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_recalib_request_check(chip, + sensor_num, false, ¬ify_check); + if (rc < 0 || !notify_check) { + pr_debug("Calib recheck re-armed rc=%d\n", rc); + chip->th_info.adc_tm_low_enable = 0; + return rc; + } + sensor_mask = 1 << sensor_num; + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_LOW_THR_INT_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("low threshold int read failed\n"); + return rc; + } + } else { + /* + * Uses the thermal sysfs registered device to disable + * the corresponding high voltage threshold which + * is triggered by low temp + */ + sensor_mask = 1 << sensor_num; + pr_debug("thermal node with mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_activate_trip_type( + chip->sensor[sensor_num].tz_dev, + ADC_TM_TRIP_HIGH_WARM, + THERMAL_TRIP_ACTIVATION_DISABLED); + if (rc < 0) { + pr_err("notify error:%d\n", sensor_num); + return rc; + } + } + list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { + client_info = list_entry(thr_list, + struct qpnp_adc_thr_client_info, list); + if (client_info->low_thr_set) { + client_info->low_thr_set = false; + client_info->notify_low_thr = true; + if (client_info->state_req_copy == + ADC_TM_HIGH_LOW_THR_ENABLE) + client_info->state_req_copy = + ADC_TM_HIGH_THR_ENABLE; + else + client_info->state_req_copy = + ADC_TM_LOW_THR_DISABLE; + } + } + qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); + + if (!chip->adc_tm_hc) { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_MULTI_MEAS_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } else { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_BTM_Mn_EN(sensor_num), + QPNP_BTM_Mn_MEAS_EN, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } + + rc = qpnp_adc_tm_enable_if_channel_meas(chip); + if (rc < 0) { + pr_err("re-enabling measurement failed\n"); + return rc; + } + + queue_work(chip->sensor[sensor_num].req_wq, + &chip->sensor[sensor_num].work); + + return rc; +} + +static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) +{ + int rc = 0, sensor_num = 0; if (qpnp_adc_tm_is_valid(chip)) return -ENODEV; + pr_debug("%s\n", __func__); + mutex_lock(&chip->adc->adc_lock); if (!chip->adc_tm_hc) { @@ -2117,159 +2314,36 @@ static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) } } - if (chip->th_info.adc_tm_high_enable) { - sensor_notify_num = chip->th_info.adc_tm_high_enable; - while (i < chip->max_channels_available) { - if ((sensor_notify_num & 0x1) == 1) - sensor_num = i; - sensor_notify_num >>= 1; - i++; - } - - btm_chan_num = chip->sensor[sensor_num].btm_channel_num; - pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", - sensor_num, chip->th_info.adc_tm_high_enable, - chip->th_info.adc_tm_low_enable, - chip->th_info.qpnp_adc_tm_meas_en); - if (!chip->sensor[sensor_num].thermal_node) { - /* For non thermal registered clients - such as usb_id, vbatt, pmic_therm */ - sensor_mask = 1 << sensor_num; - pr_debug("non thermal node - mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_recalib_request_check(chip, - sensor_num, true, ¬ify_check); - if (rc < 0 || !notify_check) { - pr_debug("Calib recheck re-armed rc=%d\n", rc); - chip->th_info.adc_tm_high_enable = 0; + while (sensor_num < chip->max_channels_available) { + if (chip->sensor[sensor_num].high_thr_triggered) { + rc = qpnp_adc_tm_disable_rearm_high_thresholds( + chip, sensor_num); + if (rc) { + pr_err("rearm threshold failed\n"); goto fail; } - } else { - /* Uses the thermal sysfs registered device to disable - the corresponding high voltage threshold which - is triggered by low temp */ - pr_debug("thermal node with mask:%x\n", sensor_mask); - } - list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { - client_info = list_entry(thr_list, - struct qpnp_adc_thr_client_info, list); - if (client_info->high_thr_set) { - client_info->high_thr_set = false; - client_info->notify_high_thr = true; - if (client_info->state_req_copy == - ADC_TM_HIGH_LOW_THR_ENABLE) - client_info->state_req_copy = - ADC_TM_LOW_THR_ENABLE; - else - client_info->state_req_copy = - ADC_TM_HIGH_THR_DISABLE; - } + chip->sensor[sensor_num].high_thr_triggered = false; } + sensor_num++; } - if (chip->th_info.adc_tm_low_enable) { - sensor_notify_num = chip->th_info.adc_tm_low_enable; - i = 0; - while (i < chip->max_channels_available) { - if ((sensor_notify_num & 0x1) == 1) - sensor_num = i; - sensor_notify_num >>= 1; - i++; - } - - btm_chan_num = chip->sensor[sensor_num].btm_channel_num; - pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", - sensor_num, chip->th_info.adc_tm_high_enable, - chip->th_info.adc_tm_low_enable, - chip->th_info.qpnp_adc_tm_meas_en); - if (!chip->sensor[sensor_num].thermal_node) { - /* For non thermal registered clients - such as usb_id, vbatt, pmic_therm */ - pr_debug("non thermal node - mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_recalib_request_check(chip, - sensor_num, false, ¬ify_check); - if (rc < 0 || !notify_check) { - pr_debug("Calib recheck re-armed rc=%d\n", rc); - chip->th_info.adc_tm_low_enable = 0; - goto fail; - } - sensor_mask = 1 << sensor_num; - rc = qpnp_adc_tm_reg_update(chip, - QPNP_ADC_TM_LOW_THR_INT_EN, - sensor_mask, false); - if (rc < 0) { - pr_err("low threshold int read failed\n"); - goto fail; - } - } else { - /* Uses the thermal sysfs registered device to disable - the corresponding low voltage threshold which - is triggered by high temp */ - pr_debug("thermal node with mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_activate_trip_type( - chip->sensor[sensor_num].tz_dev, - ADC_TM_TRIP_HIGH_WARM, - THERMAL_TRIP_ACTIVATION_DISABLED); - if (rc < 0) { - pr_err("notify error:%d\n", sensor_num); + sensor_num = 0; + while (sensor_num < chip->max_channels_available) { + if (chip->sensor[sensor_num].low_thr_triggered) { + rc = qpnp_adc_tm_disable_rearm_low_thresholds( + chip, sensor_num); + if (rc) { + pr_err("rearm threshold failed\n"); goto fail; } + chip->sensor[sensor_num].low_thr_triggered = false; } - list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { - client_info = list_entry(thr_list, - struct qpnp_adc_thr_client_info, list); - if (client_info->low_thr_set) { - /* mark the corresponding clients threshold - as not set */ - client_info->low_thr_set = false; - client_info->notify_low_thr = true; - if (client_info->state_req_copy == - ADC_TM_HIGH_LOW_THR_ENABLE) - client_info->state_req_copy = - ADC_TM_HIGH_THR_ENABLE; - else - client_info->state_req_copy = - ADC_TM_LOW_THR_DISABLE; - } - } + sensor_num++; } - qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); - - if (chip->th_info.adc_tm_high_enable || - chip->th_info.adc_tm_low_enable) { - if (!chip->adc_tm_hc) { - rc = qpnp_adc_tm_reg_update(chip, - QPNP_ADC_TM_MULTI_MEAS_EN, - sensor_mask, false); - if (rc < 0) { - pr_err("multi meas disable failed\n"); - goto fail; - } - } else { - rc = qpnp_adc_tm_reg_update(chip, - QPNP_BTM_Mn_EN(sensor_mask), - QPNP_BTM_Mn_MEAS_EN, false); - if (rc < 0) { - pr_err("multi meas disable failed\n"); - goto fail; - } - } - - rc = qpnp_adc_tm_enable_if_channel_meas(chip); - if (rc < 0) { - pr_err("re-enabling measurement failed\n"); - return rc; - } - } else - pr_debug("No threshold status enable %d for high/low??\n", - sensor_mask); - fail: mutex_unlock(&chip->adc->adc_lock); - if (chip->th_info.adc_tm_high_enable || chip->th_info.adc_tm_low_enable) - queue_work(chip->sensor[sensor_num].req_wq, - &chip->sensor[sensor_num].work); if (rc < 0 || (!chip->th_info.adc_tm_high_enable && !chip->th_info.adc_tm_low_enable)) atomic_dec(&chip->wq_cnt); @@ -2290,6 +2364,8 @@ static void qpnp_adc_tm_high_thr_work(struct work_struct *work) chip->adc_vote_enable = false; } + pr_debug("thr:0x%x\n", chip->th_info.adc_tm_high_enable); + rc = qpnp_adc_tm_read_status(chip); if (rc < 0) pr_err("adc-tm high thr work failed\n"); @@ -2393,6 +2469,8 @@ static void qpnp_adc_tm_low_thr_work(struct work_struct *work) chip->adc_vote_enable = false; } + pr_debug("thr:0x%x\n", chip->th_info.adc_tm_low_enable); + rc = qpnp_adc_tm_read_status(chip); if (rc < 0) pr_err("adc-tm low thr work failed\n"); @@ -2483,7 +2561,7 @@ static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data) static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, u8 status_low, u8 status_high, int i, - int sensor_low_notify_num, int sensor_high_notify_num) + int *sensor_low_notify_num, int *sensor_high_notify_num) { int rc = 0; u8 ctl = 0, sensor_mask = 0; @@ -2523,7 +2601,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, return IRQ_HANDLED; } } - sensor_low_notify_num |= (status_low & 0x1); + *sensor_low_notify_num |= (status_low & 0x1); + chip->sensor[i].low_thr_triggered = true; } if ((status_high & 0x1) && (ctl & QPNP_BTM_Mn_MEAS_EN) && @@ -2553,7 +2632,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, return IRQ_HANDLED; } } - sensor_high_notify_num |= (status_high & 0x1); + *sensor_high_notify_num |= (status_high & 0x1); + chip->sensor[i].high_thr_triggered = true; } } @@ -2590,7 +2670,8 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data) while (i < chip->max_channels_available) { rc = qpnp_adc_tm_rc_check_sensor_trip(chip, status_low, status_high, i, - sensor_low_notify_num, sensor_high_notify_num); + &sensor_low_notify_num, + &sensor_high_notify_num); if (rc) { pr_err("Sensor trip read failed\n"); return IRQ_HANDLED; @@ -2600,14 +2681,15 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data) i++; } - if (sensor_low_notify_num || sensor_high_notify_num) + if (sensor_low_notify_num) { atomic_inc(&chip->wq_cnt); - - if (sensor_low_notify_num) queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work); + } - if (sensor_high_notify_num) + if (sensor_high_notify_num) { + atomic_inc(&chip->wq_cnt); queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work); + } return IRQ_HANDLED; } @@ -2710,6 +2792,7 @@ int32_t qpnp_adc_tm_channel_measure(struct qpnp_adc_tm_chip *chip, channel, scale_type, dt_index); param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num; param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den; + param->adc_tm_hc = chip->adc_tm_hc; chip->adc->amux_prop->amux_channel = channel; chip->adc->amux_prop->decimation = chip->adc->adc_channels[dt_index].adc_decimation; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 49bed21b1284..4635edf0189b 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1796,6 +1796,36 @@ static void dwc3_msm_bus_vote_w(struct work_struct *w) dev_err(mdwc->dev, "Failed to reset bus bw vote %d\n", ret); } +static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc) +{ + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + int i, num_ports; + u32 reg; + + mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE); + if (mdwc->in_host_mode) { + reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1); + num_ports = HCS_MAX_PORTS(reg); + for (i = 0; i < num_ports; i++) { + reg = dwc3_msm_read_reg(mdwc->base, + USB3_PORTSC + i*0x10); + if (reg & PORT_PE) { + if (DEV_HIGHSPEED(reg) || DEV_FULLSPEED(reg)) + mdwc->hs_phy->flags |= PHY_HSFS_MODE; + else if (DEV_LOWSPEED(reg)) + mdwc->hs_phy->flags |= PHY_LS_MODE; + } + } + } else { + if (dwc->gadget.speed == USB_SPEED_HIGH || + dwc->gadget.speed == USB_SPEED_FULL) + mdwc->hs_phy->flags |= PHY_HSFS_MODE; + else if (dwc->gadget.speed == USB_SPEED_LOW) + mdwc->hs_phy->flags |= PHY_LS_MODE; + } +} + + static int dwc3_msm_suspend(struct dwc3_msm *mdwc) { int ret, i; @@ -1869,6 +1899,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc) /* disable power event irq, hs and ss phy irq is used as wake up src */ disable_irq(mdwc->pwr_event_irq); + dwc3_set_phy_speed_flags(mdwc); /* Suspend HS PHY */ usb_phy_set_suspend(mdwc->hs_phy, 1); @@ -2018,6 +2049,7 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND; } + mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE); /* Resume HS PHY */ usb_phy_set_suspend(mdwc->hs_phy, 0); @@ -2334,11 +2366,18 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc) if (IS_ERR(mdwc->bus_aggr_clk)) mdwc->bus_aggr_clk = NULL; - mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk"); - if (IS_ERR(mdwc->cfg_ahb_clk)) { - dev_err(mdwc->dev, "failed to get cfg_ahb_clk\n"); - ret = PTR_ERR(mdwc->cfg_ahb_clk); - return ret; + if (of_property_match_string(mdwc->dev->of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk"); + if (IS_ERR(mdwc->cfg_ahb_clk)) { + ret = PTR_ERR(mdwc->cfg_ahb_clk); + mdwc->cfg_ahb_clk = NULL; + if (ret != -EPROBE_DEFER) + dev_err(mdwc->dev, + "failed to get cfg_ahb_clk ret %d\n", + ret); + return ret; + } } return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 35994b827549..7d97aeb21340 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -723,7 +723,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) * due to stale trbs with HWO bit set from previous composition when update * transfer cmd is issued. */ - if (dep->number > 1) { + if (dep->number > 1 && dep->trb_pool) { memset(&dep->trb_pool[0], 0, sizeof(struct dwc3_trb) * dep->num_trbs); dbg_event(dep->number, "Clr_TRB", 0); diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 468a7bcd8dbd..5612645d7237 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -256,8 +256,14 @@ static int ipa_connect_channels(struct gsi_data_port *d_port) in_params->dir = GSI_CHAN_DIR_FROM_GSI; in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len; in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr; - in_params->xfer_scratch.last_trb_addr = - d_port->in_last_trb_addr = gsi_channel_info.last_trb_addr; + in_params->xfer_scratch.last_trb_addr_iova = + gsi_channel_info.last_trb_addr; + in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova = + gsi_channel_info.xfer_ring_base_addr; + in_params->data_buff_base_len = d_port->in_request.buf_len * + d_port->in_request.num_bufs; + in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova = + d_port->in_request.dma; in_params->xfer_scratch.const_buffer_size = gsi_channel_info.const_buffer_size; in_params->xfer_scratch.depcmd_low_addr = @@ -289,8 +295,14 @@ static int ipa_connect_channels(struct gsi_data_port *d_port) out_params->xfer_ring_len = gsi_channel_info.xfer_ring_len; out_params->xfer_ring_base_addr = + out_params->xfer_ring_base_addr_iova = gsi_channel_info.xfer_ring_base_addr; - out_params->xfer_scratch.last_trb_addr = + out_params->data_buff_base_len = d_port->out_request.buf_len * + d_port->out_request.num_bufs; + out_params->data_buff_base_addr = + out_params->data_buff_base_addr_iova = + d_port->out_request.dma; + out_params->xfer_scratch.last_trb_addr_iova = gsi_channel_info.last_trb_addr; out_params->xfer_scratch.const_buffer_size = gsi_channel_info.const_buffer_size; @@ -2787,7 +2799,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page) ipa_chnl_params->xfer_scratch.depcmd_low_addr); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10x\n", "IN LastTRB Addr Off: ", - ipa_chnl_params->xfer_scratch.last_trb_addr); + ipa_chnl_params->xfer_scratch.last_trb_addr_iova); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10u\n", "IN Buffer Size: ", ipa_chnl_params->xfer_scratch.const_buffer_size); @@ -2821,7 +2833,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page) ipa_chnl_params->xfer_scratch.depcmd_low_addr); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10x\n", "OUT LastTRB Addr Off: ", - ipa_chnl_params->xfer_scratch.last_trb_addr); + ipa_chnl_params->xfer_scratch.last_trb_addr_iova); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10u\n", "OUT Buffer Size: ", ipa_chnl_params->xfer_scratch.const_buffer_size); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 8dd65884067f..f29c275c490b 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -733,12 +733,13 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_dr = DR_UFP; if (pd->psy_type == POWER_SUPPLY_TYPE_USB || - pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) + pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) { extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, is_cable_flipped(pd)); extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1); + } } pd->rx_msg_len = 0; diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index 1bc6b6ba4517..5a29fc96f940 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -66,6 +66,9 @@ #define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */ #define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */ +#define LINESTATE_DP BIT(0) +#define LINESTATE_DM BIT(1) + unsigned int phy_tune2; module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); @@ -87,6 +90,8 @@ struct qusb_phy { int vdd_levels[3]; /* none, low, high */ int init_seq_len; int *qusb_phy_init_seq; + int host_init_seq_len; + int *qusb_phy_host_init_seq; u32 tune2_val; int tune2_efuse_bit_pos; @@ -374,6 +379,35 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, } } +static void qusb_phy_host_init(struct usb_phy *phy) +{ + u8 reg; + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + dev_dbg(phy->dev, "%s\n", __func__); + + /* Perform phy reset */ + clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); + usleep_range(100, 150); + clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); + + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq, + qphy->host_init_seq_len, 0); + + /* Ensure above write is completed before turning ON ref clk */ + wmb(); + + /* Require to get phy pll lock successfully */ + usleep_range(150, 160); + + reg = readb_relaxed(qphy->base + QUSB2PHY_PLL_COMMON_STATUS_ONE); + dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg); + if (!(reg & CORE_READY_STATUS)) { + dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg); + WARN_ON(1); + } +} + static int qusb_phy_init(struct usb_phy *phy) { struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); @@ -489,6 +523,20 @@ static void qusb_phy_shutdown(struct usb_phy *phy) qusb_phy_enable_clocks(qphy, false); } + +static u32 qusb_phy_get_linestate(struct qusb_phy *qphy) +{ + u32 linestate = 0; + + if (qphy->cable_connected) { + if (qphy->phy.flags & PHY_HSFS_MODE) + linestate |= LINESTATE_DP; + else if (qphy->phy.flags & PHY_LS_MODE) + linestate |= LINESTATE_DM; + } + return linestate; +} + /** * Performs QUSB2 PHY suspend/resume functionality. * @@ -515,8 +563,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(0x00, qphy->base + QUSB2PHY_INTR_CTRL); - linestate = readl_relaxed(qphy->base + - QUSB2PHY_INTR_STAT); + linestate = qusb_phy_get_linestate(qphy); /* * D+/D- interrupts are level-triggered, but we are * only interested if the line state changes, so enable @@ -527,14 +574,17 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) * configure the mask to trigger on D+ low OR D- high */ intr_mask = DMSE_INTERRUPT | DPSE_INTERRUPT; - if (!(linestate & DPSE_INTR_EN)) /* D+ low */ + if (!(linestate & LINESTATE_DP)) /* D+ low */ intr_mask |= DPSE_INTR_HIGH_SEL; - if (!(linestate & DMSE_INTR_EN)) /* D- low */ + if (!(linestate & LINESTATE_DM)) /* D- low */ intr_mask |= DMSE_INTR_HIGH_SEL; writel_relaxed(intr_mask, qphy->base + QUSB2PHY_INTR_CTRL); + dev_dbg(phy->dev, "%s: intr_mask = %x\n", + __func__, intr_mask); + /* Makes sure that above write goes through */ wmb(); qusb_phy_enable_clocks(qphy, false); @@ -603,6 +653,9 @@ static int qusb_phy_notify_connect(struct usb_phy *phy, dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + if (qphy->qusb_phy_host_init_seq && qphy->phy.flags & PHY_HOST_MODE) + qusb_phy_host_init(phy); + /* Set OTG VBUS Valid from HSPHY to controller */ qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, UTMI_OTG_VBUS_VALID, @@ -767,9 +820,17 @@ static int qusb_phy_probe(struct platform_device *pdev) else clk_set_rate(qphy->ref_clk, 19200000); - qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); - if (IS_ERR(qphy->cfg_ahb_clk)) - return PTR_ERR(qphy->cfg_ahb_clk); + if (of_property_match_string(pdev->dev.of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(qphy->cfg_ahb_clk)) { + ret = PTR_ERR(qphy->cfg_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "clk get failed for cfg_ahb_clk ret %d\n", ret); + return ret; + } + } qphy->phy_reset = devm_clk_get(dev, "phy_reset"); if (IS_ERR(qphy->phy_reset)) @@ -866,6 +927,23 @@ static int qusb_phy_probe(struct platform_device *pdev) } } + qphy->host_init_seq_len = of_property_count_elems_of_size(dev->of_node, + "qcom,qusb-phy-host-init-seq", + sizeof(*qphy->qusb_phy_host_init_seq)); + if (qphy->host_init_seq_len > 0) { + qphy->qusb_phy_host_init_seq = devm_kcalloc(dev, + qphy->host_init_seq_len, + sizeof(*qphy->qusb_phy_host_init_seq), + GFP_KERNEL); + if (qphy->qusb_phy_host_init_seq) + of_property_read_u32_array(dev->of_node, + "qcom,qusb-phy-host-init-seq", + qphy->qusb_phy_host_init_seq, + qphy->host_init_seq_len); + else + return -ENOMEM; + } + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", (u32 *) qphy->vdd_levels, ARRAY_SIZE(qphy->vdd_levels)); diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 50b63f912638..794bc2e33695 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -547,13 +547,16 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) clk_set_rate(phy->aux_clk, clk_round_rate(phy->aux_clk, ULONG_MAX)); - phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); - if (IS_ERR(phy->cfg_ahb_clk)) { - ret = PTR_ERR(phy->cfg_ahb_clk); - phy->cfg_ahb_clk = NULL; - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get cfg_ahb_clk\n"); - goto err; + if (of_property_match_string(pdev->dev.of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(phy->cfg_ahb_clk)) { + ret = PTR_ERR(phy->cfg_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "failed to get cfg_ahb_clk ret %d\n", ret); + goto err; + } } phy->pipe_clk = devm_clk_get(dev, "pipe_clk"); diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index c49ce06430be..ef5c96214c19 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -96,13 +96,13 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS fails to acknowledge the BTA command, it sends PANEL_ALIVE=0 status to HAL layer to reset the controller. -config FB_MSM_MDSS_EDP_PANEL +config FB_MSM_MDSS_DP_PANEL depends on FB_MSM_MDSS - bool "MDSS eDP Panel" + bool "MDSS DP Panel" ---help--- - The MDSS eDP Panel provides support for eDP host controller driver + The MDSS DP Panel provides support for DP host controller driver which runs in Video mode only and is responsible for transmitting - frame buffer from host SOC to eDP display panel. + frame buffer from host SOC to DP display panel. config FB_MSM_MDSS_MDP3 depends on FB_MSM_MDSS diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 9d25b08c753a..dccd7bcc7219 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -43,8 +43,8 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_cec_core.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o -obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o -obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o +obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o +obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c new file mode 100644 index 000000000000..07e3445c51f7 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -0,0 +1,1148 @@ +/* Copyright (c) 2012-2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/qpnp/pwm.h> +#include <linux/clk.h> +#include <linux/spinlock_types.h> +#include <linux/kthread.h> + +#include "mdss.h" +#include "mdss_dp.h" +#include "mdss_dp_util.h" +#include "mdss_debug.h" + +#define RGB_COMPONENTS 3 +#define VDDA_MIN_UV 1800000 /* uV units */ +#define VDDA_MAX_UV 1800000 /* uV units */ +#define VDDA_UA_ON_LOAD 100000 /* uA units */ +#define VDDA_UA_OFF_LOAD 100 /* uA units */ + + + +static void mdss_dp_put_dt_clk_data(struct device *dev, + struct dss_module_power *module_power) +{ + if (!module_power) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + if (module_power->clk_config) { + devm_kfree(dev, module_power->clk_config); + module_power->clk_config = NULL; + } + module_power->num_clk = 0; +} /* mdss_dp_put_dt_clk_data */ + +static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) +{ + return !strncmp(clk_name, clk_prefix, strlen(clk_prefix)); +} + +static int mdss_dp_init_clk_power_data(struct device *dev, + struct mdss_dp_drv_pdata *pdata) +{ + int num_clk = 0, i = 0, rc = 0; + int core_clk_count = 0, ctrl_clk_count = 0; + const char *core_clk = "core"; + const char *ctrl_clk = "ctrl"; + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + const char *clk_name; + + num_clk = of_property_count_strings(dev->of_node, + "clock-names"); + if (num_clk <= 0) { + pr_err("no clocks are defined\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &pdata->power_data[DP_CORE_PM]; + ctrl_power_data = &pdata->power_data[DP_CTRL_PM]; + + for (i = 0; i < num_clk; i++) { + of_property_read_string_index(dev->of_node, "clock-names", + i, &clk_name); + + if (mdss_dp_is_clk_prefix(core_clk, clk_name)) + core_clk_count++; + if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name)) + ctrl_clk_count++; + } + + /* Initialize the CORE power module */ + if (core_clk_count <= 0) { + pr_err("no core clocks are defined\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data->num_clk = core_clk_count; + core_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * + core_power_data->num_clk, GFP_KERNEL); + if (!core_power_data->clk_config) { + rc = -EINVAL; + goto exit; + } + + /* Initialize the CTRL power module */ + if (ctrl_clk_count <= 0) { + pr_err("no ctrl clocks are defined\n"); + rc = -EINVAL; + goto ctrl_clock_error; + } + + ctrl_power_data->num_clk = ctrl_clk_count; + ctrl_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * + ctrl_power_data->num_clk, GFP_KERNEL); + if (!ctrl_power_data->clk_config) { + ctrl_power_data->num_clk = 0; + rc = -EINVAL; + goto ctrl_clock_error; + } + + return rc; + +ctrl_clock_error: + mdss_dp_put_dt_clk_data(dev, core_power_data); +exit: + return rc; +} + +static int mdss_dp_get_dt_clk_data(struct device *dev, + struct mdss_dp_drv_pdata *pdata) +{ + int rc = 0, i = 0; + const char *clk_name; + int num_clk = 0; + int core_clk_index = 0, ctrl_clk_index = 0; + int core_clk_count = 0, ctrl_clk_count = 0; + const char *core_clk = "core"; + const char *ctrl_clk = "ctrl"; + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + + if (!dev || !pdata) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + rc = mdss_dp_init_clk_power_data(dev, pdata); + if (rc) { + pr_err("failed to initialize power data\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &pdata->power_data[DP_CORE_PM]; + core_clk_count = core_power_data->num_clk; + ctrl_power_data = &pdata->power_data[DP_CTRL_PM]; + ctrl_clk_count = ctrl_power_data->num_clk; + + num_clk = core_clk_count + ctrl_clk_count; + + for (i = 0; i < num_clk; i++) { + of_property_read_string_index(dev->of_node, "clock-names", + i, &clk_name); + + if (mdss_dp_is_clk_prefix(core_clk, clk_name) + && core_clk_index < core_clk_count) { + struct dss_clk *clk = + &core_power_data->clk_config[core_clk_index]; + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); + clk->type = DSS_CLK_AHB; + core_clk_index++; + } else if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name) + && ctrl_clk_index < ctrl_clk_count) { + struct dss_clk *clk = + &ctrl_power_data->clk_config[ctrl_clk_index]; + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); + ctrl_clk_index++; + if (!strcmp(clk_name, "ctrl_link_clk")) + clk->type = DSS_CLK_PCLK; + else if (!strcmp(clk_name, "ctrl_pixel_clk")) + clk->type = DSS_CLK_PCLK; + else + clk->type = DSS_CLK_AHB; + } + } + + pr_debug("Display-port clock parsing successful\n"); + +exit: + return rc; +} /* mdss_dp_get_dt_clk_data */ + +/* + * This clock control function supports enabling/disabling + * of core and ctrl power module clocks + */ +static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv, + int pm_type, bool enable) +{ + int ret = 0; + + if ((pm_type != DP_CORE_PM) + && (pm_type != DP_CTRL_PM)) { + pr_err("unsupported power module: %s\n", + __mdss_dp_pm_name(pm_type)); + return -EINVAL; + } + + if (enable) { + if ((pm_type == DP_CORE_PM) + && (dp_drv->core_clks_on)) { + pr_debug("core clks already enabled\n"); + return 0; + } + + if ((pm_type == DP_CTRL_PM) + && (dp_drv->link_clks_on)) { + pr_debug("links clks already enabled\n"); + return 0; + } + + if ((pm_type == DP_CTRL_PM) + && (!dp_drv->core_clks_on)) { + pr_debug("Need to enable core clks before link clks\n"); + + ret = msm_dss_enable_clk( + dp_drv->power_data[DP_CORE_PM].clk_config, + dp_drv->power_data[DP_CORE_PM].num_clk, 1); + if (ret) { + pr_err("failed to enable clks for %s\n", + __mdss_dp_pm_name(pm_type)); + goto error; + } else { + dp_drv->core_clks_on = true; + } + } + + ret = msm_dss_enable_clk( + dp_drv->power_data[pm_type].clk_config, + dp_drv->power_data[pm_type].num_clk, 1); + if (ret) { + pr_err("failed to enable clks for %s\n", + __mdss_dp_pm_name(pm_type)); + goto error; + } + } else { + ret = msm_dss_enable_clk( + dp_drv->power_data[pm_type].clk_config, + dp_drv->power_data[pm_type].num_clk, 0); + if (ret) { + pr_err("failed to disable clks for %s\n", + __mdss_dp_pm_name(pm_type)); + goto error; + } + } + + if (pm_type == DP_CORE_PM) + dp_drv->core_clks_on = enable; + else + dp_drv->link_clks_on = enable; + +error: + return ret; +} + +static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv, + bool enable) +{ + int i, ret = 0; + + if (dp_drv->core_power == enable) { + pr_debug("regulators already %s\n", + enable ? "enabled" : "disabled"); + return 0; + } + + if (enable) { + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { + ret = msm_dss_enable_vreg( + dp_drv->power_data[i].vreg_config, + dp_drv->power_data[i].num_vreg, 1); + if (ret) { + pr_err("failed to enable vregs for %s\n", + __mdss_dp_pm_name(i)); + goto error; + } + } + } else { + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { + ret = msm_dss_enable_vreg( + dp_drv->power_data[i].vreg_config, + dp_drv->power_data[i].num_vreg, 1); + if (ret) { + pr_err("failed to disable vregs for %s\n", + __mdss_dp_pm_name(i)); + goto error; + } + } + } + + dp_drv->core_power = enable; + +error: + return ret; +} + +static void mdss_dp_put_dt_vreg_data(struct device *dev, + struct dss_module_power *module_power) +{ + if (!module_power) { + DEV_ERR("invalid input\n"); + return; + } + + if (module_power->vreg_config) { + devm_kfree(dev, module_power->vreg_config); + module_power->vreg_config = NULL; + } + module_power->num_vreg = 0; +} /* mdss_dp_put_dt_vreg_data */ + +static int mdss_dp_get_dt_vreg_data(struct device *dev, + struct device_node *of_node, struct dss_module_power *mp, + enum dp_pm_type module) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *supply_node = NULL; + const char *pm_supply_name = NULL; + struct device_node *supply_root_node = NULL; + + if (!dev || !mp) { + pr_err("invalid input\n"); + rc = -EINVAL; + return rc; + } + + mp->num_vreg = 0; + pm_supply_name = __mdss_dp_pm_supply_node_name(module); + supply_root_node = of_get_child_by_name(of_node, pm_supply_name); + if (!supply_root_node) { + pr_err("no supply entry present: %s\n", pm_supply_name); + goto novreg; + } + + mp->num_vreg = + of_get_available_child_count(supply_root_node); + + if (mp->num_vreg == 0) { + pr_debug("no vreg\n"); + goto novreg; + } else { + pr_debug("vreg found. count=%d\n", mp->num_vreg); + } + + mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + rc = -ENOMEM; + goto error; + } + + for_each_child_of_node(supply_root_node, supply_node) { + const char *st = NULL; + /* vreg-name */ + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err("error reading name. rc=%d\n", + rc); + goto error; + } + snprintf(mp->vreg_config[i].vreg_name, + ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st); + /* vreg-min-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err("error reading min volt. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + /* vreg-max-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err("error reading max volt. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + /* enable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err("error reading enable load. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].enable_load = tmp; + + /* disable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err("error reading disable load. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].disable_load = tmp; + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d\n", + mp->vreg_config[i].vreg_name, + mp->vreg_config[i].min_voltage, + mp->vreg_config[i].max_voltage, + mp->vreg_config[i].enable_load, + mp->vreg_config[i].disable_load + ); + ++i; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(dev, mp->vreg_config); + mp->vreg_config = NULL; + } +novreg: + mp->num_vreg = 0; + + return rc; +} /* mdss_dp_get_dt_vreg_data */ + +static int mdss_dp_regulator_init(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp_drv) +{ + int rc = 0, i = 0, j = 0; + + if (!pdev || !dp_drv) { + pr_err("invalid input\n"); + return -EINVAL; + } + + for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) { + rc = msm_dss_config_vreg(&pdev->dev, + dp_drv->power_data[i].vreg_config, + dp_drv->power_data[i].num_vreg, 1); + if (rc) { + pr_err("failed to init vregs for %s\n", + __mdss_dp_pm_name(i)); + for (j = i-1; j >= DP_CORE_PM; j--) { + msm_dss_config_vreg(&pdev->dev, + dp_drv->power_data[j].vreg_config, + dp_drv->power_data[j].num_vreg, 0); + } + } + } + + return rc; +} + +void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp) +{ + /* + * To siwtch the usb3_phy to operate in DP mode, the phy and PLL + * should have the reset lines asserted + */ + mdss_dp_assert_phy_reset(&dp->ctrl_io, true); + /* Delay to make sure the assert is propagated */ + udelay(2000); + mdss_dp_switch_usb3_phy_to_dp_mode(&dp->tcsr_reg_io); + wmb(); /* ensure that the register write is successful */ + mdss_dp_assert_phy_reset(&dp->ctrl_io, false); +} + +void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp) +{ + struct dpcd_cap *cap; + struct display_timing_desc *timing; + u32 data = 0; + + timing = &dp->edid.timing[0]; + + cap = &dp->dpcd; + + data = dp->lane_cnt - 1; + data <<= 4; + + if (cap->enhanced_frame) + data |= 0x40; + + if (dp->edid.color_depth == 8) { + /* 0 == 6 bits, 1 == 8 bits */ + data |= 0x100; /* bit 8 */ + } + + if (!timing->interlaced) /* progressive */ + data |= 0x04; + + data |= 0x03; /* sycn clock & static Mvid */ + + mdss_dp_configuration_ctrl(&dp->ctrl_io, data); +} + +int mdss_dp_wait4train(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + + if (dp_drv->cont_splash) + return ret; + + ret = wait_for_completion_timeout(&dp_drv->video_comp, 30); + if (ret <= 0) { + pr_err("Link Train timedout\n"); + ret = -EINVAL; + } else { + ret = 0; + } + + pr_debug("End--\n"); + + return ret; +} + +#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3 + +static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv) +{ + struct mdss_panel_info *pinfo; + struct msm_hdmi_mode_timing_info timing = {0}; + u32 ret; + + if (!dp_drv) { + DEV_ERR("invalid input\n"); + return -EINVAL; + } + + dp_drv->ds_data.ds_registered = false; + ret = hdmi_get_supported_mode(&timing, &dp_drv->ds_data, + DEFAULT_VIDEO_RESOLUTION); + pinfo = &dp_drv->panel_data.panel_info; + + if (ret || !timing.supported || !pinfo) { + DEV_ERR("%s: invalid timing data\n", __func__); + return -EINVAL; + } + + pinfo->xres = timing.active_h; + pinfo->yres = timing.active_v; + pinfo->clk_rate = timing.pixel_freq * 1000; + + pinfo->lcdc.h_back_porch = timing.back_porch_h; + pinfo->lcdc.h_front_porch = timing.front_porch_h; + pinfo->lcdc.h_pulse_width = timing.pulse_width_h; + pinfo->lcdc.v_back_porch = timing.back_porch_v; + pinfo->lcdc.v_front_porch = timing.front_porch_v; + pinfo->lcdc.v_pulse_width = timing.pulse_width_v; + + pinfo->type = EDP_PANEL; + pinfo->pdest = DISPLAY_4; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 1; + + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + return 0; +} /* dp_init_panel_info */ + + +int mdss_dp_on(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + + if (!pdata) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + + pr_debug("++ cont_splash=%d\n", dp_drv->cont_splash); + + if (!dp_drv->cont_splash) { /* vote for clocks */ + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to start core clocks\n"); + return ret; + } + mdss_dp_phy_reset(&dp_drv->ctrl_io); + mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + mdss_dp_hpd_configure(&dp_drv->ctrl_io, true); + + mdss_dp_phy_aux_setup(&dp_drv->phy_io); + + mdss_dp_irq_enable(dp_drv); + pr_debug("irq enabled\n"); + mdss_dp_dpcd_cap_read(dp_drv); + ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); + if (ret) { + mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); + pr_err("Unabled to start link clocks\n"); + return ret; + } + + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + + reinit_completion(&dp_drv->idle_comp); + mdss_dp_fill_link_cfg(dp_drv); + mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true); + mdss_dp_config_ctrl(dp_drv); + mdss_dp_sw_mvid_nvid(&dp_drv->ctrl_io); + mdss_dp_timing_cfg(&dp_drv->ctrl_io, + &dp_drv->panel_data.panel_info); + } else { + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + } + + pr_debug("call link_training\n"); + mdss_dp_link_train(dp_drv); + + mdss_dp_wait4train(dp_drv); + + dp_drv->cont_splash = 0; + + if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) + pr_debug("mainlink ready\n"); + + pr_debug("End-\n"); + return ret; +} + +int mdss_dp_off(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + if (!dp_drv) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); + + /* wait until link training is completed */ + mutex_lock(&dp_drv->train_mutex); + + reinit_completion(&dp_drv->idle_comp); + mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); + + ret = wait_for_completion_timeout(&dp_drv->idle_comp, + msecs_to_jiffies(100)); + if (ret == 0) + pr_err("idle pattern timedout\n"); + + mdss_dp_state_ctrl(&dp_drv->ctrl_io, 0); + + mdss_dp_irq_disable(dp_drv); + + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); + + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false); + mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); + mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); + + mdss_dp_regulator_ctrl(dp_drv, false); + + pr_debug("End--: state_ctrl=%x\n", + dp_read(dp_drv->base + DP_STATE_CTRL)); + + mutex_unlock(&dp_drv->train_mutex); + return 0; +} + +static int mdss_dp_host_init(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + + if (!pdata) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + + ret = mdss_dp_regulator_ctrl(dp_drv, true); + if (ret) { + pr_err("failed to enable regulators\n"); + goto vreg_error; + } + + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to start core clocks\n"); + goto clk_error; + } + + mdss_dp_aux_init(dp_drv); + + mdss_dp_phy_reset(&dp_drv->ctrl_io); + mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_phy_initialize(dp_drv); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + + return ret; + +clk_error: + mdss_dp_regulator_ctrl(dp_drv, false); +vreg_error: + return ret; +} + +static int mdss_dp_event_handler(struct mdss_panel_data *pdata, + int event, void *arg) +{ + int rc = 0; + + pr_debug("event=%d\n", event); + switch (event) { + case MDSS_EVENT_UNBLANK: + rc = mdss_dp_on(pdata); + break; + case MDSS_EVENT_PANEL_OFF: + rc = mdss_dp_off(pdata); + break; + } + return rc; +} + +static int mdss_dp_remove(struct platform_device *pdev) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + + dp_drv = platform_get_drvdata(pdev); + + iounmap(dp_drv->ctrl_io.base); + dp_drv->ctrl_io.base = NULL; + iounmap(dp_drv->phy_io.base); + dp_drv->phy_io.base = NULL; + + return 0; +} + +static int mdss_dp_device_register(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret; + + ret = dp_init_panel_info(dp_drv); + if (ret) { + DEV_ERR("%s: dp_init_panel_info failed\n", __func__); + return ret; + } + + dp_drv->panel_data.event_handler = mdss_dp_event_handler; + + dp_drv->panel_data.panel_info.cont_splash_enabled = + dp_drv->cont_splash; + + ret = mdss_register_panel(dp_drv->pdev, &dp_drv->panel_data); + if (ret) { + dev_err(&(dp_drv->pdev->dev), "unable to register dp\n"); + return ret; + } + + pr_info("dp initialized\n"); + + return 0; +} + +/* + * Retrieve dp Resources + */ +static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp_drv) +{ + int rc = 0; + u32 index; + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index); + if (rc) { + dev_err(&pdev->dev, + "Cell-index not specified, rc=%d\n", + rc); + return rc; + } + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->ctrl_io, "dp_ctrl"); + if (rc) { + pr_err("%d unable to remap dp ctrl resources\n", + __LINE__); + return rc; + } + dp_drv->base = dp_drv->ctrl_io.base; + dp_drv->base_size = dp_drv->ctrl_io.len; + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->phy_io, "dp_phy"); + if (rc) { + pr_err("%d unable to remap dp PHY resources\n", + __LINE__); + return rc; + } + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->tcsr_reg_io, + "tcsr_regs"); + if (rc) { + pr_err("%d unable to remap dp tcsr_reg resources\n", + __LINE__); + return rc; + } + + pr_debug("DP Driver base=%p size=%x\n", + dp_drv->base, dp_drv->base_size); + + mdss_debug_register_base("dp", + dp_drv->base, dp_drv->base_size, NULL); + + return 0; +} + +static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("dp_video_ready\n"); + complete(&dp->video_comp); +} + +static void mdss_dp_idle_patterns_sent(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("idle_patterns_sent\n"); + complete(&dp->idle_comp); +} + +static void mdss_dp_do_link_train(struct mdss_dp_drv_pdata *dp) +{ + if (dp->cont_splash) + return; + + mdss_dp_link_train(dp); +} + +static void mdss_dp_event_work(struct work_struct *work) +{ + struct mdss_dp_drv_pdata *dp = NULL; + struct delayed_work *dw = to_delayed_work(work); + unsigned long flag; + u32 todo = 0; + + if (!dw) { + pr_err("invalid work structure\n"); + return; + } + + dp = container_of(dw, struct mdss_dp_drv_pdata, dwork); + + spin_lock_irqsave(&dp->event_lock, flag); + todo = dp->current_event; + dp->current_event = 0; + spin_unlock_irqrestore(&dp->event_lock, flag); + + pr_debug("todo=%x\n", todo); + + switch (todo) { + case (EV_EDID_READ): + mdss_dp_edid_read(dp, 0); + break; + case (EV_DPCD_CAP_READ): + mdss_dp_dpcd_cap_read(dp); + break; + case (EV_DPCD_STATUS_READ): + mdss_dp_dpcd_status_read(dp); + break; + case (EV_LINK_TRAIN): + mdss_dp_do_link_train(dp); + break; + case (EV_VIDEO_READY): + mdss_dp_video_ready(dp); + break; + case (EV_IDLE_PATTERNS_SENT): + mdss_dp_idle_patterns_sent(dp); + break; + default: + pr_err("Unknown event:%d\n", todo); + } +} + +static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events) +{ + spin_lock(&dp->event_lock); + dp->current_event = events; + queue_delayed_work(dp->workq, + &dp->dwork, HZ); + spin_unlock(&dp->event_lock); +} + +irqreturn_t dp_isr(int irq, void *ptr) +{ + struct mdss_dp_drv_pdata *dp = (struct mdss_dp_drv_pdata *)ptr; + unsigned char *base = dp->base; + u32 isr1, isr2, mask1, mask2; + u32 ack; + + spin_lock(&dp->lock); + isr1 = dp_read(base + DP_INTR_STATUS); + isr2 = dp_read(base + DP_INTR_STATUS2); + + mask1 = isr1 & dp->mask1; + mask2 = isr2 & dp->mask2; + + isr1 &= ~mask1; /* remove masks bit */ + isr2 &= ~mask2; + + pr_debug("isr=%x mask=%x isr2=%x mask2=%x\n", + isr1, mask1, isr2, mask2); + + ack = isr1 & EDP_INTR_STATUS1; + ack <<= 1; /* ack bits */ + ack |= mask1; + dp_write(base + DP_INTR_STATUS, ack); + + ack = isr2 & EDP_INTR_STATUS2; + ack <<= 1; /* ack bits */ + ack |= mask2; + dp_write(base + DP_INTR_STATUS2, ack); + spin_unlock(&dp->lock); + + if (isr1 & EDP_INTR_HPD) { + isr1 &= ~EDP_INTR_HPD; /* clear */ + mdss_dp_host_init(&dp->panel_data); + dp_send_events(dp, EV_LINK_TRAIN); + } + + if (isr2 & EDP_INTR_READY_FOR_VIDEO) + dp_send_events(dp, EV_VIDEO_READY); + + if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT) + dp_send_events(dp, EV_IDLE_PATTERNS_SENT); + + if (isr1 && dp->aux_cmd_busy) { + /* clear DP_AUX_TRANS_CTRL */ + dp_write(base + DP_AUX_TRANS_CTRL, 0); + /* read DP_INTERRUPT_TRANS_NUM */ + dp->aux_trans_num = + dp_read(base + DP_INTERRUPT_TRANS_NUM); + + if (dp->aux_cmd_i2c) + dp_aux_i2c_handler(dp, isr1); + else + dp_aux_native_handler(dp, isr1); + } + + return IRQ_HANDLED; +} + +static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) +{ + + spin_lock_init(&dp->event_lock); + dp->workq = create_workqueue("mdss_dp_hpd"); + if (!dp->workq) { + pr_err("%s: Error creating workqueue\n", __func__); + return -EPERM; + } + + INIT_DELAYED_WORK(&dp->dwork, mdss_dp_event_work); + return 0; +} + +static int mdss_dp_probe(struct platform_device *pdev) +{ + int ret, i; + struct mdss_dp_drv_pdata *dp_drv; + struct mdss_panel_cfg *pan_cfg = NULL; + struct mdss_util_intf *util; + + util = mdss_get_util_intf(); + if (!util) { + pr_err("Failed to get mdss utility functions\n"); + return -ENODEV; + } + + if (!util->mdp_probe_done) { + pr_err("MDP not probed yet!\n"); + return -EPROBE_DEFER; + } + + if (!pdev || !pdev->dev.of_node) { + pr_err("pdev not found for DP controller\n"); + return -ENODEV; + } + + pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP); + if (IS_ERR(pan_cfg)) { + return PTR_ERR(pan_cfg); + } else if (pan_cfg) { + pr_debug("DP as prim not supported\n"); + return -ENODEV; + } + + dp_drv = devm_kzalloc(&pdev->dev, sizeof(*dp_drv), GFP_KERNEL); + if (dp_drv == NULL) + return -ENOMEM; + + dp_drv->pdev = pdev; + dp_drv->pdev->id = 1; + dp_drv->mdss_util = util; + dp_drv->clk_on = 0; + dp_drv->aux_rate = 19200000; + dp_drv->mask1 = EDP_INTR_MASK1; + dp_drv->mask2 = EDP_INTR_MASK2; + mutex_init(&dp_drv->emutex); + spin_lock_init(&dp_drv->lock); + + ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv); + if (ret) + goto probe_err; + + /* Parse the regulator information */ + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { + ret = mdss_dp_get_dt_vreg_data(&pdev->dev, + pdev->dev.of_node, &dp_drv->power_data[i], i); + if (ret) { + pr_err("get_dt_vreg_data failed for %s. rc=%d\n", + __mdss_dp_pm_name(i), ret); + i--; + for (; i >= DP_CORE_PM; i--) + mdss_dp_put_dt_vreg_data(&pdev->dev, + &dp_drv->power_data[i]); + goto probe_err; + } + } + + ret = mdss_dp_get_dt_clk_data(&pdev->dev, dp_drv); + if (ret) { + DEV_ERR("get_dt_clk_data failed.ret=%d\n", + ret); + goto probe_err; + } + + ret = mdss_dp_regulator_init(pdev, dp_drv); + if (ret) + goto probe_err; + + ret = mdss_dp_irq_setup(dp_drv); + if (ret) + goto probe_err; + + ret = mdss_dp_event_setup(dp_drv); + if (ret) + goto probe_err; + + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to enable core clocks\n"); + goto probe_err; + } + + pr_info("ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n", + mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io), + mdss_dp_get_phy_hw_version(&dp_drv->phy_io)); + + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); + if (ret) { + pr_err("Unabled to disable core clocks\n"); + goto probe_err; + } + + dp_drv->cont_splash = dp_drv->mdss_util->panel_intf_status(DISPLAY_1, + MDSS_PANEL_INTF_EDP) ? true : false; + + platform_set_drvdata(pdev, dp_drv); + + mdss_dp_device_register(dp_drv); + + dp_drv->inited = true; + + pr_debug("done\n"); + + return 0; + +probe_err: + iounmap(dp_drv->ctrl_io.base); + iounmap(dp_drv->phy_io.base); + if (dp_drv) + devm_kfree(&pdev->dev, dp_drv); + return ret; + +} + +static const struct of_device_id msm_mdss_dp_dt_match[] = { + {.compatible = "qcom,mdss-dp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_mdss_dp_dt_match); + +static struct platform_driver mdss_dp_driver = { + .probe = mdss_dp_probe, + .remove = mdss_dp_remove, + .shutdown = NULL, + .driver = { + .name = "mdss_dp", + .of_match_table = msm_mdss_dp_dt_match, + }, +}; + +static int __init mdss_dp_init(void) +{ + int ret; + + ret = platform_driver_register(&mdss_dp_driver); + if (ret) { + pr_err("driver register failed"); + return ret; + } + + return ret; +} +module_init(mdss_dp_init); + +static void __exit mdss_dp_driver_cleanup(void) +{ + platform_driver_unregister(&mdss_dp_driver); +} +module_exit(mdss_dp_driver_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DP controller driver"); diff --git a/drivers/video/fbdev/msm/mdss_edp.h b/drivers/video/fbdev/msm/mdss_dp.h index cd83c382a227..b63318dcca06 100644 --- a/drivers/video/fbdev/msm/mdss_edp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,13 +11,23 @@ * */ -#ifndef MDSS_EDP_H -#define MDSS_EDP_H +#ifndef MDSS_DP_H +#define MDSS_DP_H +#include <linux/list.h> +#include <linux/mdss_io_util.h> +#include <linux/irqreturn.h> +#include <linux/pinctrl/consumer.h> +#include <linux/gpio.h> #include <linux/of_gpio.h> -#define edp_read(offset) readl_relaxed((offset)) -#define edp_write(offset, data) writel_relaxed((data), (offset)) +#include "mdss_hdmi_util.h" +#include "video/msm_hdmi_modes.h" +#include "mdss.h" +#include "mdss_panel.h" + +#define dp_read(offset) readl_relaxed((offset)) +#define dp_write(offset, data) writel_relaxed((data), (offset)) #define AUX_CMD_FIFO_LEN 144 #define AUX_CMD_MAX 16 @@ -73,7 +83,7 @@ #define EDP_INTR_STATUS1 \ - (EDP_INTR_HPD | EDP_INTR_AUX_I2C_DONE| \ + (EDP_INTR_AUX_I2C_DONE| \ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ @@ -94,17 +104,6 @@ #define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2) -#define EDP_MAINLINK_CTRL 0x004 -#define EDP_STATE_CTRL 0x008 -#define EDP_MAINLINK_READY 0x084 - -#define EDP_AUX_CTRL 0x300 -#define EDP_INTERRUPT_STATUS 0x308 -#define EDP_INTERRUPT_STATUS_2 0x30c -#define EDP_AUX_DATA 0x314 -#define EDP_AUX_TRANS_CTRL 0x318 -#define EDP_AUX_STATUS 0x324 - #define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510 #define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514 @@ -127,6 +126,13 @@ struct edp_buf { char i2c; /* 1 == i2c cmd, 0 == native cmd */ }; +enum dp_pm_type { + DP_CORE_PM, + DP_CTRL_PM, + DP_PHY_PM, + DP_MAX_PM +}; + #define DPCD_ENHANCED_FRAME BIT(0) #define DPCD_TPS3 BIT(1) #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) @@ -156,9 +162,10 @@ struct edp_buf { #define SINK_POWER_ON 1 #define SINK_POWER_OFF 2 -#define EDP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */ -#define EDP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */ -#define EDP_LINK_RATE_MAX EDP_LINK_RATE_270 +#define DP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */ +#define DP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */ +#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */ +#define DP_LINK_RATE_MAX DP_LINK_RATE_540 struct dpcd_cap { char major; @@ -215,7 +222,7 @@ struct edp_edid { short id_product; char version; char revision; - char video_intf; /* edp == 0x5 */ + char video_intf; /* dp == 0x5 */ char color_depth; /* 6, 8, 10, 12 and 14 bits */ char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */ char dpm; /* display power management */ @@ -227,7 +234,7 @@ struct edp_edid { struct display_timing_desc timing[4]; }; -struct edp_statistic { +struct dp_statistic { u32 intr_hpd; u32 intr_aux_i2c_done; u32 intr_wrong_addr; @@ -251,9 +258,9 @@ struct edp_statistic { #define DPCD_LINK_VOLTAGE_MAX 4 #define DPCD_LINK_PRE_EMPHASIS_MAX 4 -#define HPD_EVENT_MAX 8 +irqreturn_t dp_isr(int irq, void *ptr); -struct mdss_edp_drv_pdata { +struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); int (*off) (struct mdss_panel_data *pdata); @@ -263,10 +270,15 @@ struct mdss_edp_drv_pdata { int clk_cnt; int cont_splash; bool inited; - int delay_link_train; + bool core_power; + bool core_clks_on; + bool link_clks_on; - /* edp specific */ + /* dp specific */ unsigned char *base; + struct dss_io_data ctrl_io; + struct dss_io_data phy_io; + struct dss_io_data tcsr_reg_io; int base_size; unsigned char *mmss_cc_base; u32 mask1; @@ -275,8 +287,8 @@ struct mdss_edp_drv_pdata { struct mdss_panel_data panel_data; struct mdss_util_intf *mdss_util; - int edp_on_cnt; - int edp_off_cnt; + int dp_on_cnt; + int dp_off_cnt; u32 pixel_rate; u32 aux_rate; @@ -289,26 +301,9 @@ struct mdss_edp_drv_pdata { struct dpcd_cap dpcd; /* regulators */ - struct regulator *vdda_vreg; - - /* clocks */ - struct clk *aux_clk; - struct clk *pixel_clk; - struct clk *ahb_clk; - struct clk *link_clk; - struct clk *mdp_core_clk; + struct dss_module_power power_data[DP_MAX_PM]; int clk_on; - /* gpios */ - int gpio_panel_en; - int gpio_lvl_en; - - /* backlight */ - struct pwm_device *bl_pwm; - bool is_pwm_enabled; - int lpg_channel; - int pwm_period; - /* hpd */ int gpio_panel_hpd; enum of_gpio_flags hpd_flags; @@ -338,43 +333,51 @@ struct mdss_edp_drv_pdata { char valid_boundary; char delay_start; u32 bpp; - struct edp_statistic edp_stat; + struct dp_statistic dp_stat; /* event */ - wait_queue_head_t event_q; - u32 event_pndx; - u32 event_gndx; - u32 event_todo_list[HPD_EVENT_MAX]; + struct workqueue_struct *workq; + struct delayed_work dwork; + u32 current_event; spinlock_t event_lock; spinlock_t lock; + struct hdmi_util_ds_data ds_data; }; -int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv); - -void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *edp); -int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *edp); -void mdss_edp_edid_read(struct mdss_edp_drv_pdata *edp, int block); -int mdss_edp_link_train(struct mdss_edp_drv_pdata *edp); -void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *edp, u32 isr); -void edp_aux_native_handler(struct mdss_edp_drv_pdata *edp, u32 isr); -void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep); - -void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep); -void mdss_edp_sink_power_down(struct mdss_edp_drv_pdata *ep); -void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state); -int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state); -void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up); -void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep); - -void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base); - -#endif /* MDSS_EDP_H */ +static inline const char *__mdss_dp_pm_name(enum dp_pm_type module) +{ + switch (module) { + case DP_CORE_PM: return "DP_CORE_PM"; + case DP_CTRL_PM: return "DP_CTRL_PM"; + case DP_PHY_PM: return "DP_PHY_PM"; + default: return "???"; + } +} + +static inline const char *__mdss_dp_pm_supply_node_name( + enum dp_pm_type module) +{ + switch (module) { + case DP_CORE_PM: return "qcom,core-supply-entries"; + case DP_CTRL_PM: return "qcom,ctrl-supply-entries"; + case DP_PHY_PM: return "qcom,phy-supply-entries"; + default: return "???"; + } +} + +void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); + +void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); +int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp); +void mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp, int block); +int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp); +void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *dp, u32 isr); +void dp_aux_native_handler(struct mdss_dp_drv_pdata *dp, u32 isr); +void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep); + +void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep); +void mdss_dp_sink_power_down(struct mdss_dp_drv_pdata *ep); +void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up); +void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep); + +#endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_edp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index d3a060d6f288..39f11a8c35d1 100644 --- a/drivers/video/fbdev/msm/mdss_edp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013, 2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,6 +11,8 @@ * */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -28,17 +30,14 @@ #include <linux/of_gpio.h> #include <linux/clk/msm-clk.h> -#include <mach/hardware.h> -#include <mach/gpio.h> -#include <mach/dma.h> - #include "mdss_panel.h" -#include "mdss_edp.h" +#include "mdss_dp.h" +#include "mdss_dp_util.h" /* * edp buffer operation */ -static char *edp_buf_init(struct edp_buf *eb, char *buf, int size) +static char *dp_buf_init(struct edp_buf *eb, char *buf, int size) { eb->start = buf; eb->size = size; @@ -50,7 +49,7 @@ static char *edp_buf_init(struct edp_buf *eb, char *buf, int size) return eb->data; } -static char *edp_buf_reset(struct edp_buf *eb) +static char *dp_buf_reset(struct edp_buf *eb) { eb->data = eb->start; eb->len = 0; @@ -59,23 +58,23 @@ static char *edp_buf_reset(struct edp_buf *eb) return eb->data; } -static char *edp_buf_push(struct edp_buf *eb, int len) +static char *dp_buf_push(struct edp_buf *eb, int len) { eb->data += len; eb->len += len; return eb->data; } -static int edp_buf_trailing(struct edp_buf *eb) +static int dp_buf_trailing(struct edp_buf *eb) { return (int)(eb->end - eb->data); } /* - * edp aux edp_buf_add_cmd: + * edp aux dp_buf_add_cmd: * NO native and i2c command mix allowed */ -static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) +static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) { char data; char *bp, *cp; @@ -86,7 +85,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) else len = cmd->len + 4; - if (edp_buf_trailing(eb) < len) + if (dp_buf_trailing(eb) < len) return 0; /* @@ -111,7 +110,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) for (i = 0; i < cmd->len; i++) *bp++ = *cp++; } - edp_buf_push(eb, len); + dp_buf_push(eb, len); if (cmd->i2c) eb->i2c++; @@ -121,7 +120,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) return cmd->len - 1; } -static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) +static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) { u32 data; char *dp; @@ -140,8 +139,8 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) data &= 0x00ff00; /* index = 0, write */ if (cnt == 0) data |= BIT(31); /* INDEX_WRITE */ - pr_debug("%s: data=%x\n", __func__, data); - edp_write(base + EDP_AUX_DATA, data); + pr_debug("data=%x\n", data); + dp_write(base + DP_AUX_DATA, data); cnt++; dp++; } @@ -151,13 +150,13 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) data |= BIT(8); /* I2C */ data |= BIT(9); /* GO */ - pr_debug("%s: data=%x\n", __func__, data); - edp_write(base + EDP_AUX_TRANS_CTRL, data); + pr_debug("data=%x\n", data); + dp_write(base + DP_AUX_TRANS_CTRL, data); return tp->len; } -static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) +static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) { u32 data; char *dp; @@ -166,15 +165,15 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) data = 0; /* index = 0 */ data |= BIT(31); /* INDEX_WRITE */ data |= BIT(0); /* read */ - edp_write(base + EDP_AUX_DATA, data); + dp_write(base + DP_AUX_DATA, data); dp = rp->data; /* discard first byte */ - data = edp_read(base + EDP_AUX_DATA); + data = dp_read(base + DP_AUX_DATA); for (i = 0; i < len; i++) { - data = edp_read(base + EDP_AUX_DATA); - pr_debug("%s: data=%x\n", __func__, data); + data = dp_read(base + DP_AUX_DATA); + pr_debug("data=%x\n", data); *dp++ = (char)((data >> 8) & 0xff); } @@ -182,7 +181,7 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) return len; } -static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, +static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmd) { struct edp_cmd *cm; @@ -193,14 +192,14 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, ep->aux_cmd_busy = 1; tp = &ep->txp; - edp_buf_reset(tp); + dp_buf_reset(tp); cm = cmd; while (cm) { - pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n", - __func__, cm->i2c, cm->read, cm->addr, cm->len, + pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n", + cm->i2c, cm->read, cm->addr, cm->len, cm->next); - ret = edp_buf_add_cmd(tp, cm); + ret = dp_buf_add_cmd(tp, cm); if (ret <= 0) break; if (cm->next == 0) @@ -215,7 +214,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - len = edp_cmd_fifo_tx(&ep->txp, ep->base); + len = dp_cmd_fifo_tx(&ep->txp, ep->base); wait_for_completion(&ep->aux_comp); @@ -229,7 +228,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, return ret; } -static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, +static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { struct edp_cmd *cm; @@ -242,16 +241,16 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, tp = &ep->txp; rp = &ep->rxp; - edp_buf_reset(tp); - edp_buf_reset(rp); + dp_buf_reset(tp); + dp_buf_reset(rp); cm = cmds; len = 0; while (cm) { - pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n", - __func__, cm->i2c, cm->read, cm->addr, cm->len, + pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n", + cm->i2c, cm->read, cm->addr, cm->len, cm->next); - ret = edp_buf_add_cmd(tp, cm); + ret = dp_buf_add_cmd(tp, cm); len += cm->len; if (ret <= 0) break; @@ -267,12 +266,12 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - edp_cmd_fifo_tx(tp, ep->base); + dp_cmd_fifo_tx(tp, ep->base); wait_for_completion(&ep->aux_comp); if (ep->aux_error_num == EDP_AUX_ERR_NONE) - ret = edp_cmd_fifo_rx(rp, len, ep->base); + ret = dp_cmd_fifo_rx(rp, len, ep->base); else ret = ep->aux_error_num; @@ -282,10 +281,10 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, return ret; } -void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr) +void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - pr_debug("%s: isr=%x\n", __func__, isr); + pr_debug("isr=%x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) ep->aux_error_num = EDP_AUX_ERR_NONE; @@ -299,10 +298,10 @@ void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr) complete(&ep->aux_comp); } -void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr) +void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - pr_debug("%s: isr=%x\n", __func__, isr); + pr_debug("isr=%x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) { if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)) @@ -325,7 +324,7 @@ void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr) complete(&ep->aux_comp); } -static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr, +static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, char *buf, int len, int i2c) { struct edp_cmd cmd; @@ -337,10 +336,10 @@ static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return edp_aux_write_cmds(ep, &cmd); + return dp_aux_write_cmds(ep, &cmd); } -static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, +static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, int len, int i2c) { struct edp_cmd cmd; @@ -352,7 +351,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return edp_aux_read_cmds(ep, &cmd); + return dp_aux_read_cmds(ep, &cmd); } /* @@ -360,7 +359,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, */ static char edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; -int edp_edid_buf_error(char *buf, int len) +int dp_edid_buf_error(char *buf, int len) { char *bp; int i; @@ -368,7 +367,7 @@ int edp_edid_buf_error(char *buf, int len) bp = buf; if (len < 128) { - pr_err("%s: Error: len=%x\n", __func__, len); + pr_err("Error: len=%x\n", len); return -EINVAL; } @@ -376,12 +375,12 @@ int edp_edid_buf_error(char *buf, int len) csum += *bp++; if (csum != 0) { - pr_err("%s: Error: csum=%x\n", __func__, csum); + pr_err("Error: csum=%x\n", csum); return -EINVAL; } if (strncmp(buf, edid_hdr, strlen(edid_hdr))) { - pr_err("%s: Error: header\n", __func__); + pr_err("Error: header\n"); return -EINVAL; } @@ -389,7 +388,7 @@ int edp_edid_buf_error(char *buf, int len) } -void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) +void dp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) { char *bp; char data; @@ -407,10 +406,10 @@ void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) edid->id_name[2] = 'A' + data - 1; edid->id_name[3] = 0; - pr_debug("%s: edid manufacturer = %s\n", __func__, edid->id_name); + pr_debug("edid manufacturer = %s\n", edid->id_name); } -void edp_extract_edid_product(struct edp_edid *edid, char *buf) +void dp_extract_edid_product(struct edp_edid *edid, char *buf) { char *bp; u32 data; @@ -423,25 +422,25 @@ void edp_extract_edid_product(struct edp_edid *edid, char *buf) data <<= 8; edid->id_product |= data; - pr_debug("%s: edid product = 0x%x\n", __func__, edid->id_product); + pr_debug("edid product = 0x%x\n", edid->id_product); }; -void edp_extract_edid_version(struct edp_edid *edid, char *buf) +void dp_extract_edid_version(struct edp_edid *edid, char *buf) { edid->version = buf[0x12]; edid->revision = buf[0x13]; - pr_debug("%s: edid version = %d.%d\n", __func__, edid->version, + pr_debug("edid version = %d.%d\n", edid->version, edid->revision); }; -void edp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf) +void dp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf) { edid->ext_block_cnt = buf[0x7e]; - pr_debug("%s: edid extension = %d\n", __func__, + pr_debug("edid extension = %d\n", edid->ext_block_cnt); }; -void edp_extract_edid_video_support(struct edp_edid *edid, char *buf) +void dp_extract_edid_video_support(struct edp_edid *edid, char *buf) { char *bp; @@ -454,14 +453,14 @@ void edp_extract_edid_video_support(struct edp_edid *edid, char *buf) edid->color_depth *= 2; edid->color_depth += 4; } - pr_debug("%s: Digital Video intf=%d color_depth=%d\n", - __func__, edid->video_intf, edid->color_depth); + pr_debug("Digital Video intf=%d color_depth=%d\n", + edid->video_intf, edid->color_depth); } else { - pr_err("%s: Error, Analog video interface\n", __func__); + pr_err("Error, Analog video interface\n"); } }; -void edp_extract_edid_feature(struct edp_edid *edid, char *buf) +void dp_extract_edid_feature(struct edp_edid *edid, char *buf) { char *bp; char data; @@ -481,11 +480,11 @@ void edp_extract_edid_feature(struct edp_edid *edid, char *buf) } } - pr_debug("%s: edid dpm=%d color_format=%d\n", __func__, + pr_debug("edid dpm=%d color_format=%d\n", edid->dpm, edid->color_format); }; -void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, +void dp_extract_edid_detailed_timing_description(struct edp_edid *edid, char *buf) { char *bp; @@ -589,22 +588,22 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, } } - pr_debug("%s: pixel_clock = %d\n", __func__, dp->pclk); + pr_debug("pixel_clock = %d\n", dp->pclk); - pr_debug("%s: horizontal=%d, blank=%d, porch=%d, sync=%d\n" - , __func__, dp->h_addressable, dp->h_blank, + pr_debug("horizontal=%d, blank=%d, porch=%d, sync=%d\n", + dp->h_addressable, dp->h_blank, dp->h_fporch, dp->h_sync_pulse); - pr_debug("%s: vertical=%d, blank=%d, porch=%d, vsync=%d\n" - , __func__, dp->v_addressable, dp->v_blank, + pr_debug("vertical=%d, blank=%d, porch=%d, vsync=%d\n", + dp->v_addressable, dp->v_blank, dp->v_fporch, dp->v_sync_pulse); - pr_debug("%s: panel size in mm, width=%d height=%d\n", __func__, + pr_debug("panel size in mm, width=%d height=%d\n", dp->width_mm, dp->height_mm); - pr_debug("%s: panel border horizontal=%d vertical=%d\n", __func__, + pr_debug("panel border horizontal=%d vertical=%d\n", dp->h_border, dp->v_border); - pr_debug("%s: flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n" - , __func__, dp->interlaced, dp->stereo, + pr_debug("flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n", + dp->interlaced, dp->stereo, dp->sync_type, dp->sync_separate); - pr_debug("%s: polarity vsync=%d, hsync=%d", __func__, + pr_debug("polarity vsync=%d, hsync=%d", dp->vsync_pol, dp->hsync_pol); } @@ -629,67 +628,67 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, * 0, 75 }; */ -static int edp_aux_chan_ready(struct mdss_edp_drv_pdata *ep) +static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) { int cnt, ret; char data = 0; for (cnt = 5; cnt; cnt--) { - ret = edp_aux_write_buf(ep, 0x50, &data, 1, 1); - pr_debug("%s: ret=%d\n", __func__, ret); + ret = dp_aux_write_buf(ep, 0x50, &data, 1, 1); + pr_debug("ret=%d\n", ret); if (ret >= 0) break; msleep(100); } if (cnt <= 0) { - pr_err("%s: aux chan NOT ready\n", __func__); - return 0; + pr_err("aux chan NOT ready\n"); + return -EIO; } - return 1; + return 0; } -static int edp_sink_edid_read(struct mdss_edp_drv_pdata *ep, int block) +static int dp_sink_edid_read(struct mdss_dp_drv_pdata *ep, int block) { struct edp_buf *rp; int cnt, rlen; int ret = 0; - ret = edp_aux_chan_ready(ep); - if (ret == 0) { - pr_err("%s: aux chan NOT ready\n", __func__); + ret = dp_aux_chan_ready(ep); + if (ret) { + pr_err("aux chan NOT ready\n"); return ret; } for (cnt = 5; cnt; cnt--) { - rlen = edp_aux_read_buf(ep, 0x50, 128, 1); + rlen = dp_aux_read_buf(ep, 0x50, 128, 1); if (rlen > 0) { - pr_debug("%s: rlen=%d\n", __func__, rlen); + pr_debug("rlen=%d\n", rlen); rp = &ep->rxp; - if (!edp_edid_buf_error(rp->data, rp->len)) + if (!dp_edid_buf_error(rp->data, rp->len)) break; } } if (cnt <= 0) { - pr_err("%s: Failed\n", __func__); + pr_err("Failed\n"); return -EINVAL; } - edp_extract_edid_manufacturer(&ep->edid, rp->data); - edp_extract_edid_product(&ep->edid, rp->data); - edp_extract_edid_version(&ep->edid, rp->data); - edp_extract_edid_ext_block_cnt(&ep->edid, rp->data); - edp_extract_edid_video_support(&ep->edid, rp->data); - edp_extract_edid_feature(&ep->edid, rp->data); - edp_extract_edid_detailed_timing_description(&ep->edid, rp->data); + dp_extract_edid_manufacturer(&ep->edid, rp->data); + dp_extract_edid_product(&ep->edid, rp->data); + dp_extract_edid_version(&ep->edid, rp->data); + dp_extract_edid_ext_block_cnt(&ep->edid, rp->data); + dp_extract_edid_video_support(&ep->edid, rp->data); + dp_extract_edid_feature(&ep->edid, rp->data); + dp_extract_edid_detailed_timing_description(&ep->edid, rp->data); return 128; } -static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, +static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, int len) { char *bp; @@ -698,9 +697,9 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, struct edp_buf *rp; int rlen; - rlen = edp_aux_read_buf(ep, 0, len, 0); + rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { - pr_err("%s: edp aux read failed\n", __func__); + pr_err("edp aux read failed\n"); return; } rp = &ep->rxp; @@ -712,14 +711,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->minor = data & 0x0f; if (--rlen <= 0) return; - pr_debug("%s: version: %d.%d\n", __func__, cap->major, cap->minor); + pr_debug("version: %d.%d\n", cap->major, cap->minor); data = *bp++; /* byte 1 */ /* 162, 270 and 540 MB, symbol rate, NOT bit rate */ cap->max_link_rate = data; if (--rlen <= 0) return; - pr_debug("%s: link_rate=%d\n", __func__, cap->max_link_rate); + pr_debug("link_rate=%d\n", cap->max_link_rate); data = *bp++; /* byte 2 */ if (data & BIT(7)) @@ -731,24 +730,24 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->max_lane_count = data; if (--rlen <= 0) return; - pr_debug("%s: lane_count=%d\n", __func__, cap->max_lane_count); + pr_debug("lane_count=%d\n", cap->max_lane_count); data = *bp++; /* byte 3 */ if (data & BIT(0)) { cap->flags |= DPCD_MAX_DOWNSPREAD_0_5; - pr_debug("%s: max_downspread\n", __func__); + pr_debug("max_downspread\n"); } if (data & BIT(6)) { cap->flags |= DPCD_NO_AUX_HANDSHAKE; - pr_debug("%s: NO Link Training\n", __func__); + pr_debug("NO Link Training\n"); } if (--rlen <= 0) return; data = *bp++; /* byte 4 */ cap->num_rx_port = (data & BIT(0)) + 1; - pr_debug("%s: rx_ports=%d", __func__, cap->num_rx_port); + pr_debug("rx_ports=%d", cap->num_rx_port); if (--rlen <= 0) return; @@ -760,14 +759,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, data = *bp++; /* byte 8 */ if (data & BIT(1)) { cap->flags |= DPCD_PORT_0_EDID_PRESENTED; - pr_debug("%s: edid presented\n", __func__); + pr_debug("edid presented\n"); } if (--rlen <= 0) return; data = *bp++; /* byte 9 */ cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("%s: lane_buf_size=%d", __func__, cap->rx_port0_buf_size); + pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size); if (--rlen <= 0) return; @@ -779,19 +778,19 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; if (cap->i2c_speed_ctrl > 0) - pr_debug("%s: i2c_rate=%d", __func__, cap->i2c_speed_ctrl); + pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl); if (--rlen <= 0) return; data = *bp++; /* byte 13 */ cap->scrambler_reset = data & BIT(0); - pr_debug("%s: scrambler_reset=%d\n", __func__, + pr_debug("scrambler_reset=%d\n", cap->scrambler_reset); if (data & BIT(1)) cap->enhanced_frame++; - pr_debug("%s: enhanced_framing=%d\n", __func__, + pr_debug("enhanced_framing=%d\n", cap->enhanced_frame); if (--rlen <= 0) return; @@ -801,11 +800,11 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->training_read_interval = 4000; /* us */ else cap->training_read_interval = 4000 * data; /* us */ - pr_debug("%s: training_interval=%d\n", __func__, + pr_debug("training_interval=%d\n", cap->training_read_interval); } -static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) +static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len) { char *bp; char data; @@ -813,11 +812,11 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) struct edp_buf *rp; int rlen; - pr_debug("%s: len=%d", __func__, len); + pr_debug("len=%d", len); /* skip byte 0x200 and 0x201 */ - rlen = edp_aux_read_buf(ep, 0x202, len, 0); + rlen = dp_aux_read_buf(ep, 0x202, len, 0); if (rlen < len) { - pr_err("%s: edp aux read failed\n", __func__); + pr_err("edp aux read failed\n"); return 0; } rp = &ep->rxp; @@ -860,7 +859,7 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) return len; } -static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep) +static int dp_cap_lane_rate_set(struct mdss_dp_drv_pdata *ep) { char buf[4]; int len = 0; @@ -868,17 +867,17 @@ static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep) cap = &ep->dpcd; - pr_debug("%s: bw=%x lane=%d\n", __func__, ep->link_rate, ep->lane_cnt); + pr_debug("bw=%x lane=%d\n", ep->link_rate, ep->lane_cnt); buf[0] = ep->link_rate; buf[1] = ep->lane_cnt; if (cap->enhanced_frame) buf[1] |= 0x80; - len = edp_aux_write_buf(ep, 0x100, buf, 2, 0); + len = dp_aux_write_buf(ep, 0x100, buf, 2, 0); return len; } -static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level, +static int dp_lane_set_write(struct mdss_dp_drv_pdata *ep, int voltage_level, int pre_emphasis_level) { int i; @@ -895,21 +894,21 @@ static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level, for (i = 0; i < 4; i++) buf[i] = voltage_level | pre_emphasis_level; - pr_debug("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level); - return edp_aux_write_buf(ep, 0x103, buf, 4, 0); + pr_debug("p|v=0x%x", voltage_level | pre_emphasis_level); + return dp_aux_write_buf(ep, 0x103, buf, 4, 0); } -static int edp_train_pattern_set_write(struct mdss_edp_drv_pdata *ep, +static int dp_train_pattern_set_write(struct mdss_dp_drv_pdata *ep, int pattern) { char buf[4]; - pr_debug("%s: pattern=%x\n", __func__, pattern); + pr_debug("pattern=%x\n", pattern); buf[0] = pattern; - return edp_aux_write_buf(ep, 0x102, buf, 1, 0); + return dp_aux_write_buf(ep, 0x102, buf, 1, 0); } -static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) +static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; @@ -927,7 +926,7 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) data |= ep->link_status.lane_01_status; } - pr_debug("%s: data=%x mask=%x\n", __func__, data, mask); + pr_debug("data=%x mask=%x\n", data, mask); data &= mask; if (data == mask) /* all done */ return 1; @@ -935,15 +934,15 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) return 0; } -static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) +static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; - pr_debug("%s:\n", __func__); + pr_debug("Entered++\n"); if (!ep->link_status.interlane_align_done) { /* not align */ - pr_err("%s: interlane align failed\n", __func__); + pr_err("interlane align failed\n"); return 0; } @@ -960,7 +959,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) data |= ep->link_status.lane_01_status; } - pr_debug("%s: data=%x mask=%x\n", __func__, data, mask); + pr_debug("data=%x mask=%x\n", data, mask); data &= mask; if (data == mask)/* all done */ @@ -969,7 +968,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) return 0; } -void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) +void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep) { int i; int max = 0; @@ -977,8 +976,8 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) /* use the max level across lanes */ for (i = 0; i < ep->lane_cnt; i++) { - pr_debug("%s: lane=%d req_voltage_swing=%d", - __func__, i, ep->link_status.req_voltage_swing[i]); + pr_debug("lane=%d req_voltage_swing=%d", + i, ep->link_status.req_voltage_swing[i]); if (max < ep->link_status.req_voltage_swing[i]) max = ep->link_status.req_voltage_swing[i]; } @@ -988,18 +987,18 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) /* use the max level across lanes */ max = 0; for (i = 0; i < ep->lane_cnt; i++) { - pr_debug(" %s: lane=%d req_pre_emphasis=%d", - __func__, i, ep->link_status.req_pre_emphasis[i]); + pr_debug("lane=%d req_pre_emphasis=%d", + i, ep->link_status.req_pre_emphasis[i]); if (max < ep->link_status.req_pre_emphasis[i]) max = ep->link_status.req_pre_emphasis[i]; } ep->p_level = max; - pr_debug("%s: v_level=%d, p_level=%d", __func__, + pr_debug("v_level=%d, p_level=%d", ep->v_level, ep->p_level); } -static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train) +static void dp_host_train_set(struct mdss_dp_drv_pdata *ep, int train) { int bit, cnt; u32 data; @@ -1007,20 +1006,20 @@ static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train) bit = 1; bit <<= (train - 1); - pr_debug("%s: bit=%d train=%d\n", __func__, bit, train); - edp_write(ep->base + EDP_STATE_CTRL, bit); + pr_debug("bit=%d train=%d\n", bit, train); + dp_write(ep->base + DP_STATE_CTRL, bit); bit = 8; bit <<= (train - 1); cnt = 10; while (cnt--) { - data = edp_read(ep->base + EDP_MAINLINK_READY); + data = dp_read(ep->base + DP_MAINLINK_READY); if (data & bit) break; } if (cnt == 0) - pr_err("%s: set link_train=%d failed\n", __func__, train); + pr_err("set link_train=%d failed\n", train); } char vm_pre_emphasis[4][4] = { @@ -1038,38 +1037,38 @@ char vm_voltage_swing[4][4] = { {0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; -static void edp_voltage_pre_emphasise_set(struct mdss_edp_drv_pdata *ep) +static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *ep) { u32 value0 = 0; u32 value1 = 0; - pr_debug("%s: v=%d p=%d\n", __func__, ep->v_level, ep->p_level); + pr_debug("v=%d p=%d\n", ep->v_level, ep->p_level); value0 = vm_pre_emphasis[(int)(ep->v_level)][(int)(ep->p_level)]; value1 = vm_voltage_swing[(int)(ep->v_level)][(int)(ep->p_level)]; /* Configure host and panel only if both values are allowed */ if (value0 != 0xFF && value1 != 0xFF) { - edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0); - edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1); - pr_debug("%s: value0=0x%x value1=0x%x", __func__, + dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0); + dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1); + pr_debug("value0=0x%x value1=0x%x", value0, value1); - edp_lane_set_write(ep, ep->v_level, ep->p_level); + dp_lane_set_write(ep, ep->v_level, ep->p_level); } } -static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) +static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) { int tries, old_v_level; int ret = 0; int usleep_time; - pr_debug("%s:", __func__); + pr_debug("Entered++"); - edp_host_train_set(ep, 0x01); /* train_1 */ - edp_voltage_pre_emphasise_set(ep); - edp_train_pattern_set_write(ep, 0x21); /* train_1 */ + dp_host_train_set(ep, 0x01); /* train_1 */ + dp_voltage_pre_emphasise_set(ep); + dp_train_pattern_set_write(ep, 0x21); /* train_1 */ tries = 0; old_v_level = ep->v_level; @@ -1077,8 +1076,8 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - edp_link_status_read(ep, 6); - if (edp_sink_clock_recovery_done(ep)) { + dp_link_status_read(ep, 6); + if (dp_sink_clock_recovery_done(ep)) { ret = 0; break; } @@ -1099,39 +1098,39 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) old_v_level = ep->v_level; } - edp_sink_train_set_adjust(ep); - edp_voltage_pre_emphasise_set(ep); + dp_sink_train_set_adjust(ep); + dp_voltage_pre_emphasise_set(ep); } return ret; } -static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep) +static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) { int tries; int ret = 0; int usleep_time; char pattern; - pr_debug("%s:", __func__); + pr_debug("Entered++"); if (ep->dpcd.flags & DPCD_TPS3) pattern = 0x03; else pattern = 0x02; - edp_host_train_set(ep, pattern); /* train_2 */ - edp_voltage_pre_emphasise_set(ep); - edp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */ + dp_host_train_set(ep, pattern); /* train_2 */ + dp_voltage_pre_emphasise_set(ep); + dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */ tries = 0; while (1) { usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - edp_link_status_read(ep, 6); + dp_link_status_read(ep, 6); - if (edp_sink_channel_eq_done(ep)) { + if (dp_sink_channel_eq_done(ep)) { ret = 0; break; } @@ -1142,14 +1141,14 @@ static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep) break; } - edp_sink_train_set_adjust(ep); - edp_voltage_pre_emphasise_set(ep); + dp_sink_train_set_adjust(ep); + dp_voltage_pre_emphasise_set(ep); } return ret; } -static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) +static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep) { u32 prate, lrate; int rate, lane, max_lane; @@ -1164,7 +1163,7 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) prate *= ep->bpp; prate /= 8; /* byte */ - if (rate > EDP_LINK_RATE_162 && rate <= EDP_LINK_RATE_MAX) { + if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) { rate -= 4; /* reduce rate */ changed++; } @@ -1179,13 +1178,13 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) lrate /= 10; /* byte, 10 bits --> 8 bits */ lrate *= lane; - pr_debug("%s: new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n", - __func__, lrate, prate, rate, lane, ep->pixel_rate, ep->bpp); + pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n", + lrate, prate, rate, lane, ep->pixel_rate, ep->bpp); if (lrate > prate) { ep->link_rate = rate; ep->lane_cnt = lane; - pr_debug("%s: new rate=%d %d\n", __func__, rate, lane); + pr_debug("new rate=%d %d\n", rate, lane); return 0; } } @@ -1194,90 +1193,95 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) return -EINVAL; } -static void edp_clear_training_pattern(struct mdss_edp_drv_pdata *ep) +static int mdss_dp_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) +{ + int ret; + + ret = dp_aux_write_buf(ep, 0x600, &state, 1, 0); + pr_debug("state=%d ret=%d\n", state, ret); + return ret; +} + +static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep) { int usleep_time; - pr_debug("%s:\n", __func__); - edp_train_pattern_set_write(ep, 0); + + pr_debug("Entered++\n"); + dp_train_pattern_set_write(ep, 0); usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); } -static int edp_aux_link_train(struct mdss_edp_drv_pdata *ep) +static int dp_aux_link_train(struct mdss_dp_drv_pdata *ep) { int ret = 0; int usleep_time; - ret = edp_aux_chan_ready(ep); - if (ret == 0) { - pr_err("%s: LINK Train failed: aux chan NOT ready\n", __func__); + ret = dp_aux_chan_ready(ep); + if (ret) { + pr_err("LINK Train failed: aux chan NOT ready\n"); complete(&ep->train_comp); return ret; } - edp_write(ep->base + EDP_MAINLINK_CTRL, 0x1); + dp_write(ep->base + DP_MAINLINK_CTRL, 0x1); - mdss_edp_sink_power_state(ep, SINK_POWER_ON); + mdss_dp_sink_power_state(ep, SINK_POWER_ON); train_start: ep->v_level = 0; /* start from default level */ ep->p_level = 0; - edp_cap_lane_rate_set(ep); - mdss_edp_config_ctrl(ep); - mdss_edp_lane_power_ctrl(ep, 1); + dp_cap_lane_rate_set(ep); - mdss_edp_state_ctrl(ep, 0); - edp_clear_training_pattern(ep); + dp_clear_training_pattern(ep); usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - ret = edp_start_link_train_1(ep); + ret = dp_start_link_train_1(ep); if (ret < 0) { - if (edp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(ep) == 0) { goto train_start; } else { - pr_err("%s: Training 1 failed\n", __func__); + pr_err("Training 1 failed\n"); ret = -1; goto clear; } } - pr_debug("%s: Training 1 completed successfully\n", __func__); + pr_debug("Training 1 completed successfully\n"); - mdss_edp_state_ctrl(ep, 0); - edp_clear_training_pattern(ep); - ret = edp_start_link_train_2(ep); + dp_clear_training_pattern(ep); + ret = dp_start_link_train_2(ep); if (ret < 0) { - if (edp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(ep) == 0) { goto train_start; } else { - pr_err("%s: Training 2 failed\n", __func__); + pr_err("Training 2 failed\n"); ret = -1; goto clear; } } - pr_debug("%s: Training 2 completed successfully\n", __func__); + pr_debug("Training 2 completed successfully\n"); - mdss_edp_state_ctrl(ep, ST_SEND_VIDEO); clear: - edp_clear_training_pattern(ep); + dp_clear_training_pattern(ep); complete(&ep->train_comp); return ret; } -void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *ep) +void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) { - edp_sink_capability_read(ep, 16); + dp_sink_capability_read(ep, 16); } -int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep) +int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) { struct dpcd_link_status *sp; int ret = 0; /* not sync */ - ret = edp_link_status_read(ep, 6); + ret = dp_link_status_read(ep, 6); if (ret) { sp = &ep->link_status; @@ -1287,45 +1291,34 @@ int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep) return ret; } -void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep) +void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep) { struct display_timing_desc *dp; dp = &ep->edid.timing[0]; - ep->pixel_rate = dp->pclk; ep->lane_cnt = ep->dpcd.max_lane_count; - ep->link_rate = ep->dpcd.max_link_rate; - pr_debug("%s: pclk=%d rate=%d lane=%d\n", __func__, + pr_debug("pclk=%d rate=%d lane=%d\n", ep->pixel_rate, ep->link_rate, ep->lane_cnt); } -void mdss_edp_edid_read(struct mdss_edp_drv_pdata *ep, int block) +void mdss_dp_edid_read(struct mdss_dp_drv_pdata *ep, int block) { - edp_sink_edid_read(ep, block); -} - -int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state) -{ - int ret; - - ret = edp_aux_write_buf(ep, 0x600, &state, 1, 0); - pr_debug("%s: state=%d ret=%d\n", __func__, state, ret); - return ret; + dp_sink_edid_read(ep, block); } -int mdss_edp_link_train(struct mdss_edp_drv_pdata *ep) +int mdss_dp_link_train(struct mdss_dp_drv_pdata *ep) { int ret; mutex_lock(&ep->train_mutex); - ret = edp_aux_link_train(ep); + ret = dp_aux_link_train(ep); mutex_unlock(&ep->train_mutex); return ret; } -void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep) +void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep) { mutex_init(&ep->aux_mutex); mutex_init(&ep->train_mutex); @@ -1336,6 +1329,6 @@ void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep) complete(&ep->train_comp); /* make non block at first time */ complete(&ep->video_comp); /* make non block at first time */ - edp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf)); - edp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf)); + dp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf)); + dp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf)); } diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c new file mode 100644 index 000000000000..c1d29987a5fa --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/io.h> +#include <linux/delay.h> + +#include "mdss_dp_util.h" + +struct mdss_hw mdss_dp_hw = { + .hw_ndx = MDSS_HW_EDP, + .ptr = NULL, + .irq_handler = dp_isr, +}; + +/* DP retrieve ctrl HW version */ +u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io) +{ + return readl_relaxed(ctrl_io->base + DP_HW_VERSION); +} + +/* DP retrieve phy HW version */ +u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io) +{ + return readl_relaxed(phy_io->base + DP_PHY_REVISION_ID3); +} +/* DP PHY SW reset */ +void mdss_dp_phy_reset(struct dss_io_data *ctrl_io) +{ + writel_relaxed(0x04, ctrl_io->base + DP_PHY_CTRL); /* bit 2 */ + udelay(1000); + writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL); +} + +void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io) +{ + writel_relaxed(0x01, tcsr_reg_io->base + TCSR_USB3_DP_PHYMODE); +} + +/* DP PHY assert reset for PHY and PLL */ +void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert) +{ + if (assert) { + /* assert reset line for PHY and PLL */ + writel_relaxed(0x5, + ctrl_io->base + DP_PHY_CTRL); /* bit 0 & 2 */ + } else { + /* remove assert for PLL and PHY reset line */ + writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL); + } +} + +/* reset AUX */ +void mdss_dp_aux_reset(struct dss_io_data *ctrl_io) +{ + u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL); + + aux_ctrl |= BIT(1); + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); + udelay(1000); + aux_ctrl &= ~BIT(1); + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); +} + +/* reset DP Mainlink */ +void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io) +{ + u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL); + + mainlink_ctrl |= BIT(1); + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); + udelay(1000); + mainlink_ctrl &= ~BIT(1); + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); +} + +/* Configure HPD */ +void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable) +{ + if (enable) { + u32 reftimer = + readl_relaxed(ctrl_io->base + DP_DP_HPD_REFTIMER); + + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_ACK); + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_MASK); + + /* Enabling REFTIMER */ + reftimer |= BIT(16); + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_REFTIMER); + /* Enable HPD */ + writel_relaxed(0x1, ctrl_io->base + DP_DP_HPD_CTRL); + } else { + /*Disable HPD */ + writel_relaxed(0x0, ctrl_io->base + DP_DP_HPD_CTRL); + } +} + +/* Enable/Disable AUX controller */ +void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable) +{ + u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL); + + if (enable) + aux_ctrl |= BIT(0); + else + aux_ctrl &= ~BIT(0); + + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); +} + +/* DP Mainlink controller*/ +void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable) +{ + u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL); + + if (enable) + mainlink_ctrl |= BIT(0); + else + mainlink_ctrl &= ~BIT(0); + + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); +} + +int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which) +{ + u32 data; + int cnt = 10; + + while (--cnt) { + /* DP_MAINLINK_READY */ + data = readl_relaxed(dp->base + DP_MAINLINK_READY); + if (data & which) { + pr_debug("which=%x ready\n", which); + return 1; + } + udelay(1000); + } + pr_err("which=%x NOT ready\n", which); + + return 0; +} + +/* DP Configuration controller*/ +void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data) +{ + writel_relaxed(data, ctrl_io->base + DP_CONFIGURATION_CTRL); +} + +/* DP state controller*/ +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data) +{ + writel_relaxed(data, ctrl_io->base + DP_STATE_CTRL); +} + +void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io, + struct mdss_panel_info *pinfo) +{ + u32 total_ver, total_hor; + u32 data; + + pr_debug("width=%d hporch= %d %d %d\n", + pinfo->xres, pinfo->lcdc.h_back_porch, + pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width); + + pr_debug("height=%d vporch= %d %d %d\n", + pinfo->yres, pinfo->lcdc.v_back_porch, + pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width); + + total_hor = pinfo->xres + pinfo->lcdc.h_back_porch + + pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width; + + total_ver = pinfo->yres + pinfo->lcdc.v_back_porch + + pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width; + + data = total_ver; + data <<= 16; + data |= total_hor; + /* DP_TOTAL_HOR_VER */ + writel_relaxed(data, ctrl_io->base + DP_TOTAL_HOR_VER); + + data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width); + data <<= 16; + data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width); + /* DP_START_HOR_VER_FROM_SYNC */ + writel_relaxed(data, ctrl_io->base + DP_START_HOR_VER_FROM_SYNC); + + data = pinfo->lcdc.v_pulse_width; + data <<= 16; + data |= pinfo->lcdc.h_pulse_width; + /* DP_HSYNC_VSYNC_WIDTH_POLARITY */ + writel_relaxed(data, ctrl_io->base + DP_HSYNC_VSYNC_WIDTH_POLARITY); + + data = pinfo->yres; + data <<= 16; + data |= pinfo->xres; + /* DP_ACTIVE_HOR_VER */ + writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER); +} + +void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io) +{ + writel_relaxed(0x37, ctrl_io->base + DP_SOFTWARE_MVID); + writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID); +} + +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map) +{ + u8 bits_per_lane = 2; + u32 lane_map = ((l_map.lane0 << (bits_per_lane * 0)) + || (l_map.lane1 << (bits_per_lane * 1)) + || (l_map.lane2 << (bits_per_lane * 2)) + || (l_map.lane3 << (bits_per_lane * 3))); + writel_relaxed(lane_map, + ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); +} + +void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io) +{ + writel_relaxed(0x3d, phy_io->base + DP_PHY_PD_CTL); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG1); + writel_relaxed(0x00, phy_io->base + DP_PHY_AUX_CFG3); + writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG4); + writel_relaxed(0x26, phy_io->base + DP_PHY_AUX_CFG5); + writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG6); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG7); + writel_relaxed(0xbb, phy_io->base + DP_PHY_AUX_CFG8); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG9); + writel_relaxed(0x1f, phy_io->base + DP_PHY_AUX_INTERRUPT_MASK); +} + +int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + + mdss_dp_hw.irq_info = mdss_intr_line(); + if (mdss_dp_hw.irq_info == NULL) { + pr_err("Failed to get mdss irq information\n"); + return -ENODEV; + } + + mdss_dp_hw.ptr = (void *)(dp_drv); + + ret = dp_drv->mdss_util->register_irq(&mdss_dp_hw); + if (ret) + pr_err("mdss_register_irq failed.\n"); + + return ret; +} + +void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv) +{ + unsigned long flags; + + spin_lock_irqsave(&dp_drv->lock, flags); + writel_relaxed(dp_drv->mask1, dp_drv->base + DP_INTR_STATUS); + writel_relaxed(dp_drv->mask2, dp_drv->base + DP_INTR_STATUS2); + spin_unlock_irqrestore(&dp_drv->lock, flags); + + dp_drv->mdss_util->enable_irq(&mdss_dp_hw); +} + +void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv) +{ + unsigned long flags; + + spin_lock_irqsave(&dp_drv->lock, flags); + writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS); + writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS2); + spin_unlock_irqrestore(&dp_drv->lock, flags); + + dp_drv->mdss_util->disable_irq(&mdss_dp_hw); +} diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h new file mode 100644 index 000000000000..d9064cafad9a --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DP_UTIL_H__ +#define __DP_UTIL_H__ + +#include "mdss_dp.h" + +/* DP_TX Registers */ +#define DP_HW_VERSION (0x00000000) +#define DP_SW_RESET (0x00000010) +#define DP_PHY_CTRL (0x00000014) +#define DP_CLK_CTRL (0x00000018) +#define DP_CLK_ACTIVE (0x0000001C) +#define DP_INTR_STATUS (0x00000020) +#define DP_INTR_STATUS2 (0x00000024) +#define DP_INTR_STATUS3 (0x00000028) + +#define DP_DP_HPD_CTRL (0x00000200) +#define DP_DP_HPD_INT_STATUS (0x00000204) +#define DP_DP_HPD_INT_ACK (0x00000208) +#define DP_DP_HPD_INT_MASK (0x0000020C) +#define DP_DP_HPD_REFTIMER (0x00000218) +#define DP_DP_HPD_EVENT_TIME_0 (0x0000021C) +#define DP_DP_HPD_EVENT_TIME_1 (0x00000220) +#define DP_AUX_CTRL (0x00000230) +#define DP_AUX_DATA (0x00000234) +#define DP_AUX_TRANS_CTRL (0x00000238) +#define DP_AUX_STATUS (0x00000244) + +#define DP_INTERRUPT_TRANS_NUM (0x000002A0) + +#define DP_MAINLINK_CTRL (0x00000400) +#define DP_STATE_CTRL (0x00000404) +#define DP_CONFIGURATION_CTRL (0x00000408) +#define DP_SOFTWARE_MVID (0x00000410) +#define DP_SOFTWARE_NVID (0x00000418) +#define DP_TOTAL_HOR_VER (0x0000041C) +#define DP_START_HOR_VER_FROM_SYNC (0x00000420) +#define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424) +#define DP_ACTIVE_HOR_VER (0x00000428) + +#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438) + +#define DP_MAINLINK_READY (0x00000440) + +/*DP PHY Register offsets */ +#define DP_PHY_REVISION_ID0 (0x00000000) +#define DP_PHY_REVISION_ID1 (0x00000004) +#define DP_PHY_REVISION_ID2 (0x00000008) +#define DP_PHY_REVISION_ID3 (0x0000000C) + +#define DP_PHY_CFG (0x00000010) +#define DP_PHY_PD_CTL (0x00000014) +#define DP_PHY_MODE (0x00000018) + +#define DP_PHY_AUX_CFG0 (0x0000001C) +#define DP_PHY_AUX_CFG1 (0x00000020) +#define DP_PHY_AUX_CFG2 (0x00000024) +#define DP_PHY_AUX_CFG3 (0x00000028) +#define DP_PHY_AUX_CFG4 (0x0000002C) +#define DP_PHY_AUX_CFG5 (0x00000030) +#define DP_PHY_AUX_CFG6 (0x00000034) +#define DP_PHY_AUX_CFG7 (0x00000038) +#define DP_PHY_AUX_CFG8 (0x0000003C) +#define DP_PHY_AUX_CFG9 (0x00000040) +#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) +#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) + +#define TCSR_USB3_DP_PHYMODE 0x48 + +struct lane_mapping { + char lane0; + char lane1; + char lane2; + char lane3; +}; + +u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io); +u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io); +void mdss_dp_aux_reset(struct dss_io_data *ctrl_io); +void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io); +void mdss_dp_phy_reset(struct dss_io_data *ctrl_io); +void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io); +void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert); +void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io); +void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map); +int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which); +void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io, + struct mdss_panel_info *pinfo); +void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data); +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); +int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io); + +#endif /* __DP_UTIL_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_edp.c b/drivers/video/fbdev/msm/mdss_edp.c deleted file mode 100644 index add757c34e50..000000000000 --- a/drivers/video/fbdev/msm/mdss_edp.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/time.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/of_platform.h> -#include <linux/of_gpio.h> -#include <linux/gpio.h> -#include <linux/err.h> -#include <linux/regulator/consumer.h> -#include <linux/qpnp/pwm.h> -#include <linux/clk.h> -#include <linux/spinlock_types.h> -#include <linux/kthread.h> -#include <mach/hardware.h> -#include <mach/dma.h> - -#include "mdss.h" -#include "mdss_edp.h" -#include "mdss_debug.h" - -#define RGB_COMPONENTS 3 -#define VDDA_MIN_UV 1800000 /* uV units */ -#define VDDA_MAX_UV 1800000 /* uV units */ -#define VDDA_UA_ON_LOAD 100000 /* uA units */ -#define VDDA_UA_OFF_LOAD 100 /* uA units */ - -static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv); -/* - * Init regulator needed for edp, 8974_l12 - */ -static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda"); - if (IS_ERR(edp_drv->vdda_vreg)) { - pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__, - PTR_ERR(edp_drv->vdda_vreg)); - return -ENODEV; - } - - ret = regulator_set_voltage(edp_drv->vdda_vreg, - VDDA_MIN_UV, VDDA_MAX_UV); - if (ret) { - pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__, - ret); - return -EINVAL; - } - - ret = mdss_edp_regulator_on(edp_drv); - if (ret) - return ret; - - return 0; -} - -/* - * Set uA and enable vdda - */ -static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD); - if (ret < 0) { - pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__); - return ret; - } - - ret = regulator_enable(edp_drv->vdda_vreg); - if (ret) { - pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__); - return ret; - } - - return 0; -} - -/* - * Disable vdda and set uA - */ -static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - ret = regulator_disable(edp_drv->vdda_vreg); - if (ret) { - pr_err("%s: Failed to disable vdda_vreg regulator.\n", - __func__); - return ret; - } - - ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD); - if (ret < 0) { - pr_err("%s: vdda_vreg set regulator mode failed.\n", - __func__); - return ret; - } - - return 0; -} - -/* - * Enables the gpio that supply power to the panel and enable the backlight - */ -static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node, - "gpio-panel-en", 0); - if (!gpio_is_valid(edp_drv->gpio_panel_en)) { - pr_err("%s: gpio_panel_en=%d not specified\n", __func__, - edp_drv->gpio_panel_en); - goto gpio_err; - } - - ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable"); - if (ret) { - pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n", - __func__, ret); - return ret; - } - - ret = gpio_direction_output(edp_drv->gpio_panel_en, 1); - if (ret) { - pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n", - __func__, ret); - goto gpio_free; - } - - return 0; - -gpio_free: - gpio_free(edp_drv->gpio_panel_en); -gpio_err: - return -ENODEV; -} - -static int mdss_edp_gpio_lvl_en(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_lvl_en = of_get_named_gpio(edp_drv->pdev->dev.of_node, - "gpio-lvl-en", 0); - if (!gpio_is_valid(edp_drv->gpio_lvl_en)) { - pr_err("%s: gpio_lvl_en=%d not specified\n", __func__, - edp_drv->gpio_lvl_en); - ret = -ENODEV; - goto gpio_err; - } - - ret = gpio_request(edp_drv->gpio_lvl_en, "lvl_enable"); - if (ret) { - pr_err("%s: Request reset gpio_lvl_en failed, ret=%d\n", - __func__, ret); - return ret; - } - - ret = gpio_direction_output(edp_drv->gpio_lvl_en, 1); - if (ret) { - pr_err("%s: Set direction for gpio_lvl_en failed, ret=%d\n", - __func__, ret); - goto gpio_free; - } - - return ret; - -gpio_free: - gpio_free(edp_drv->gpio_lvl_en); -gpio_err: - return ret; -} - -static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,panel-pwm-period", &edp_drv->pwm_period); - if (ret) { - pr_warn("%s: panel pwm period is not specified, %d", __func__, - edp_drv->pwm_period); - edp_drv->pwm_period = -EINVAL; - } - - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,panel-lpg-channel", &edp_drv->lpg_channel); - if (ret) { - pr_warn("%s: panel lpg channel is not specified, %d", __func__, - edp_drv->lpg_channel); - edp_drv->lpg_channel = -EINVAL; - } - - if (edp_drv->pwm_period != -EINVAL && - edp_drv->lpg_channel != -EINVAL) { - edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel, - "lcd-backlight"); - if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) { - pr_err("%s: pwm request failed", __func__); - edp_drv->bl_pwm = NULL; - return -EIO; - } - } else { - edp_drv->bl_pwm = NULL; - } - - return 0; -} - -void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level) -{ - int ret = 0; - struct mdss_edp_drv_pdata *edp_drv = NULL; - int bl_max; - int period_ns; - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data); - if (!edp_drv) { - pr_err("%s: Invalid input data\n", __func__); - return; - } - - if (edp_drv->bl_pwm != NULL) { - bl_max = edp_drv->panel_data.panel_info.bl_max; - if (bl_level > bl_max) - bl_level = bl_max; - - /* In order to avoid overflow, use the microsecond version - * of pwm_config if the pwm_period is greater than or equal - * to 1 second. - */ - if (edp_drv->pwm_period >= USEC_PER_SEC) { - ret = pwm_config_us(edp_drv->bl_pwm, - bl_level * edp_drv->pwm_period / bl_max, - edp_drv->pwm_period); - if (ret) { - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - return; - } - } else { - period_ns = edp_drv->pwm_period * NSEC_PER_USEC; - ret = pwm_config(edp_drv->bl_pwm, - bl_level * period_ns / bl_max, - period_ns); - if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", - __func__, ret); - return; - } - } - - if (edp_drv->is_pwm_enabled) { - pwm_disable(edp_drv->bl_pwm); - edp_drv->is_pwm_enabled = 0; - } - - ret = pwm_enable(edp_drv->bl_pwm); - if (ret) { - pr_err("%s: pwm_enable() failed err=%d\n", __func__, - ret); - return; - } - edp_drv->is_pwm_enabled = 1; - } -} - -int mdss_edp_mainlink_ready(struct mdss_edp_drv_pdata *ep, u32 which) -{ - u32 data; - int cnt = 10; - - while (--cnt) { - data = edp_read(ep->base + 0x84); /* EDP_MAINLINK_READY */ - if (data & which) { - pr_debug("%s: which=%x ready\n", __func__, which); - return 1; - } - usleep_range(1000, 1000); - } - pr_err("%s: which=%x NOT ready\n", __func__, which); - - return 0; -} - -void mdss_edp_mainlink_reset(struct mdss_edp_drv_pdata *ep) -{ - edp_write(ep->base + 0x04, 0x02); /* EDP_MAINLINK_CTRL */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x04, 0); /* EDP_MAINLINK_CTRL */ -} - -void mdss_edp_mainlink_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - u32 data; - - data = edp_read(ep->base + 0x04); - data &= ~BIT(0); - - if (enable) - data |= 0x1; - - edp_write(ep->base + 0x04, data); -} - -void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state) -{ - edp_write(ep->base + EDP_STATE_CTRL, state); -} - -void mdss_edp_aux_reset(struct mdss_edp_drv_pdata *ep) -{ - /* reset AUX */ - edp_write(ep->base + 0x300, BIT(1)); /* EDP_AUX_CTRL */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x300, 0); /* EDP_AUX_CTRL */ -} - -void mdss_edp_aux_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - u32 data; - - data = edp_read(ep->base + 0x300); - if (enable) - data |= 0x01; - else - data |= ~0x01; - edp_write(ep->base + 0x300, data); /* EDP_AUX_CTRL */ -} - -void mdss_edp_phy_pll_reset(struct mdss_edp_drv_pdata *ep) -{ - /* EDP_PHY_CTRL */ - edp_write(ep->base + 0x74, 0x005); /* bit 0, 2 */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x74, 0x000); /* EDP_PHY_CTRL */ -} - -int mdss_edp_phy_pll_ready(struct mdss_edp_drv_pdata *ep) -{ - int cnt; - u32 status = 0; - - cnt = 100; - while (--cnt) { - status = edp_read(ep->base + 0x6c0); - if (status & 0x01) - break; - usleep_range(100, 100); - } - - pr_debug("%s: PLL cnt=%d status=%x\n", __func__, cnt, (int)status); - - if (cnt <= 0) { - pr_err("%s: PLL NOT ready\n", __func__); - return 0; - } else - return 1; -} - -int mdss_edp_phy_ready(struct mdss_edp_drv_pdata *ep) -{ - u32 status; - - status = edp_read(ep->base + 0x598); - status &= 0x01; - - return status; -} - -void mdss_edp_phy_power_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - if (enable) { - /* EDP_PHY_EDPPHY_GLB_PD_CTL */ - edp_write(ep->base + 0x52c, 0x3f); - /* EDP_PHY_EDPPHY_GLB_CFG */ - edp_write(ep->base + 0x528, 0x1); - /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */ - edp_write(ep->base + 0x620, 0xf); - } else { - /* EDP_PHY_EDPPHY_GLB_PD_CTL */ - edp_write(ep->base + 0x52c, 0xc0); - } -} - -void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up) -{ - int i, off, max_lane; - u32 data; - - max_lane = ep->lane_cnt; - - if (up) - data = 0; /* power up */ - else - data = 0x7; /* power down */ - - /* EDP_PHY_EDPPHY_LNn_PD_CTL */ - for (i = 0; i < max_lane; i++) { - off = 0x40 * i; - edp_write(ep->base + 0x404 + off , data); - } - - /* power down un used lane */ - data = 0x7; /* power down */ - for (i = max_lane; i < EDP_MAX_LANE; i++) { - off = 0x40 * i; - edp_write(ep->base + 0x404 + off , data); - } -} - -void mdss_edp_clock_synchrous(struct mdss_edp_drv_pdata *ep, int sync) -{ - u32 data; - u32 color; - - /* EDP_MISC1_MISC0 */ - data = edp_read(ep->base + 0x02c); - - if (sync) - data |= 0x01; - else - data &= ~0x01; - - /* only legacy rgb mode supported */ - color = 0; /* 6 bits */ - if (ep->edid.color_depth == 8) - color = 0x01; - else if (ep->edid.color_depth == 10) - color = 0x02; - else if (ep->edid.color_depth == 12) - color = 0x03; - else if (ep->edid.color_depth == 16) - color = 0x04; - - color <<= 5; /* bit 5 to bit 7 */ - - data |= color; - /* EDP_MISC1_MISC0 */ - edp_write(ep->base + 0x2c, data); -} - -/* voltage mode and pre emphasis cfg */ -void mdss_edp_phy_vm_pe_init(struct mdss_edp_drv_pdata *ep) -{ - /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */ - edp_write(ep->base + 0x510, 0x3); /* vm only */ - /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */ - edp_write(ep->base + 0x514, 0x64); - /* EDP_PHY_EDPPHY_GLB_MISC9 */ - edp_write(ep->base + 0x518, 0x6c); -} - -void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep) -{ - struct dpcd_cap *cap; - struct display_timing_desc *dp; - u32 data = 0; - - dp = &ep->edid.timing[0]; - - cap = &ep->dpcd; - - data = ep->lane_cnt - 1; - data <<= 4; - - if (cap->enhanced_frame) - data |= 0x40; - - if (ep->edid.color_depth == 8) { - /* 0 == 6 bits, 1 == 8 bits */ - data |= 0x100; /* bit 8 */ - } - - if (!dp->interlaced) /* progressive */ - data |= 0x04; - - data |= 0x03; /* sycn clock & static Mvid */ - - edp_write(ep->base + 0xc, data); /* EDP_CONFIGURATION_CTRL */ -} - -static void mdss_edp_sw_mvid_nvid(struct mdss_edp_drv_pdata *ep) -{ - edp_write(ep->base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */ - edp_write(ep->base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */ -} - -static void mdss_edp_timing_cfg(struct mdss_edp_drv_pdata *ep) -{ - struct mdss_panel_info *pinfo; - u32 total_ver, total_hor; - u32 data; - - pinfo = &ep->panel_data.panel_info; - - pr_debug("%s: width=%d hporch= %d %d %d\n", __func__, - pinfo->xres, pinfo->lcdc.h_back_porch, - pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width); - - pr_debug("%s: height=%d vporch= %d %d %d\n", __func__, - pinfo->yres, pinfo->lcdc.v_back_porch, - pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width); - - total_hor = pinfo->xres + pinfo->lcdc.h_back_porch + - pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width; - - total_ver = pinfo->yres + pinfo->lcdc.v_back_porch + - pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width; - - data = total_ver; - data <<= 16; - data |= total_hor; - edp_write(ep->base + 0x1c, data); /* EDP_TOTAL_HOR_VER */ - - data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width); - data <<= 16; - data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width); - edp_write(ep->base + 0x20, data); /* EDP_START_HOR_VER_FROM_SYNC */ - - data = pinfo->lcdc.v_pulse_width; - data <<= 16; - data |= pinfo->lcdc.h_pulse_width; - edp_write(ep->base + 0x24, data); /* EDP_HSYNC_VSYNC_WIDTH_POLARITY */ - - data = pinfo->yres; - data <<= 16; - data |= pinfo->xres; - edp_write(ep->base + 0x28, data); /* EDP_ACTIVE_HOR_VER */ -} - -int mdss_edp_wait4train(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - if (edp_drv->cont_splash) - return ret; - - ret = wait_for_completion_timeout(&edp_drv->video_comp, 30); - if (ret <= 0) { - pr_err("%s: Link Train timedout\n", __func__); - ret = -EINVAL; - } else { - ret = 0; - } - - pr_debug("%s:\n", __func__); - - return ret; -} - -static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv); -static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv); - -int mdss_edp_on(struct mdss_panel_data *pdata) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - int ret = 0; - - if (!pdata) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, - panel_data); - - pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash); - - if (!edp_drv->cont_splash) { /* vote for clocks */ - mdss_edp_phy_pll_reset(edp_drv); - mdss_edp_aux_reset(edp_drv); - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_aux_ctrl(edp_drv, 1); - - ret = mdss_edp_prepare_clocks(edp_drv); - if (ret) - return ret; - - mdss_edp_phy_power_ctrl(edp_drv, 1); - - ret = mdss_edp_clk_enable(edp_drv); - if (ret) { - mdss_edp_unprepare_clocks(edp_drv); - return ret; - } - - mdss_edp_phy_pll_ready(edp_drv); - - mdss_edp_lane_power_ctrl(edp_drv, 1); - - mdss_edp_clock_synchrous(edp_drv, 1); - mdss_edp_phy_vm_pe_init(edp_drv); - mdss_edp_config_ctrl(edp_drv); - mdss_edp_sw_mvid_nvid(edp_drv); - mdss_edp_timing_cfg(edp_drv); - - gpio_set_value(edp_drv->gpio_panel_en, 1); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_set_value(edp_drv->gpio_lvl_en, 1); - - reinit_completion(&edp_drv->idle_comp); - mdss_edp_mainlink_ctrl(edp_drv, 1); - } else { - mdss_edp_aux_ctrl(edp_drv, 1); - } - - mdss_edp_irq_enable(edp_drv); - - if (edp_drv->delay_link_train) { - mdss_edp_link_train(edp_drv); - edp_drv->delay_link_train = 0; - } - - mdss_edp_wait4train(edp_drv); - - edp_drv->cont_splash = 0; - - pr_debug("%s:-\n", __func__); - return ret; -} - -int mdss_edp_off(struct mdss_panel_data *pdata) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - int ret = 0; - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, - panel_data); - if (!edp_drv) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash); - - /* wait until link training is completed */ - mutex_lock(&edp_drv->train_mutex); - - reinit_completion(&edp_drv->idle_comp); - mdss_edp_state_ctrl(edp_drv, ST_PUSH_IDLE); - - ret = wait_for_completion_timeout(&edp_drv->idle_comp, - msecs_to_jiffies(100)); - if (ret == 0) - pr_err("%s: idle pattern timedout\n", __func__); - - mdss_edp_state_ctrl(edp_drv, 0); - - mdss_edp_sink_power_state(edp_drv, SINK_POWER_OFF); - - mdss_edp_irq_disable(edp_drv); - - gpio_set_value(edp_drv->gpio_panel_en, 0); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_set_value(edp_drv->gpio_lvl_en, 0); - if (edp_drv->bl_pwm != NULL) - pwm_disable(edp_drv->bl_pwm); - edp_drv->is_pwm_enabled = 0; - - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_mainlink_ctrl(edp_drv, 0); - - mdss_edp_lane_power_ctrl(edp_drv, 0); - mdss_edp_phy_power_ctrl(edp_drv, 0); - - mdss_edp_clk_disable(edp_drv); - mdss_edp_unprepare_clocks(edp_drv); - - mdss_edp_aux_ctrl(edp_drv, 0); - - pr_debug("%s-: state_ctrl=%x\n", __func__, - edp_read(edp_drv->base + 0x8)); - - mutex_unlock(&edp_drv->train_mutex); - return 0; -} - -static int mdss_edp_event_handler(struct mdss_panel_data *pdata, - int event, void *arg) -{ - int rc = 0; - - pr_debug("%s: event=%d\n", __func__, event); - switch (event) { - case MDSS_EVENT_UNBLANK: - rc = mdss_edp_on(pdata); - break; - case MDSS_EVENT_PANEL_OFF: - rc = mdss_edp_off(pdata); - break; - } - return rc; -} - -/* - * Converts from EDID struct to mdss_panel_info - */ -static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv) -{ - struct display_timing_desc *dp; - struct mdss_panel_info *pinfo; - - dp = &edp_drv->edid.timing[0]; - pinfo = &edp_drv->panel_data.panel_info; - - pinfo->clk_rate = dp->pclk; - pr_debug("%s: pclk=%d\n", __func__, pinfo->clk_rate); - - pinfo->xres = dp->h_addressable + dp->h_border * 2; - pinfo->yres = dp->v_addressable + dp->v_border * 2; - - pr_debug("%s: x=%d y=%d\n", __func__, pinfo->xres, pinfo->yres); - - pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \ - - dp->h_sync_pulse; - pinfo->lcdc.h_front_porch = dp->h_fporch; - pinfo->lcdc.h_pulse_width = dp->h_sync_pulse; - - pr_debug("%s: hporch= %d %d %d\n", __func__, - pinfo->lcdc.h_back_porch, pinfo->lcdc.h_front_porch, - pinfo->lcdc.h_pulse_width); - - pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \ - - dp->v_sync_pulse; - pinfo->lcdc.v_front_porch = dp->v_fporch; - pinfo->lcdc.v_pulse_width = dp->v_sync_pulse; - - pr_debug("%s: vporch= %d %d %d\n", __func__, - pinfo->lcdc.v_back_porch, pinfo->lcdc.v_front_porch, - pinfo->lcdc.v_pulse_width); - - pinfo->type = EDP_PANEL; - pinfo->pdest = DISPLAY_1; - pinfo->wait_cycle = 0; - pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS; - pinfo->fb_num = 2; - - pinfo->lcdc.border_clr = 0; /* black */ - pinfo->lcdc.underflow_clr = 0xff; /* blue */ - pinfo->lcdc.hsync_skew = 0; -} - -static int mdss_edp_remove(struct platform_device *pdev) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - - edp_drv = platform_get_drvdata(pdev); - - gpio_free(edp_drv->gpio_panel_en); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_free(edp_drv->gpio_lvl_en); - mdss_edp_regulator_off(edp_drv); - iounmap(edp_drv->base); - iounmap(edp_drv->mmss_cc_base); - edp_drv->base = NULL; - - return 0; -} - -static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - u32 tmp; - - mdss_edp_edid2pinfo(edp_drv); - edp_drv->panel_data.panel_info.bl_min = 1; - edp_drv->panel_data.panel_info.bl_max = 255; - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,mdss-brightness-max-level", &tmp); - edp_drv->panel_data.panel_info.brightness_max = - (!ret ? tmp : MDSS_MAX_BL_BRIGHTNESS); - - edp_drv->panel_data.panel_info.edp.frame_rate = - DEFAULT_FRAME_RATE;/* 60 fps */ - - edp_drv->panel_data.event_handler = mdss_edp_event_handler; - edp_drv->panel_data.set_backlight = mdss_edp_set_backlight; - - edp_drv->panel_data.panel_info.cont_splash_enabled = - edp_drv->cont_splash; - - ret = mdss_register_panel(edp_drv->pdev, &edp_drv->panel_data); - if (ret) { - dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n"); - return ret; - } - - pr_info("%s: eDP initialized\n", __func__); - - return 0; -} - -/* - * Retrieve edp base address - */ -static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv) -{ - struct resource *res; - - res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM, - "edp_base"); - if (!res) { - pr_err("%s: Unable to get the MDSS EDP resources", __func__); - return -ENOMEM; - } - - edp_drv->base_size = resource_size(res); - edp_drv->base = ioremap(res->start, resource_size(res)); - if (!edp_drv->base) { - pr_err("%s: Unable to remap EDP resources", __func__); - return -ENOMEM; - } - - pr_debug("%s: drv=%x base=%x size=%x\n", __func__, - (int)edp_drv, (int)edp_drv->base, edp_drv->base_size); - - mdss_debug_register_base("edp", - edp_drv->base, edp_drv->base_size, NULL); - - return 0; -} - -static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata - *edp_drv) -{ - struct resource *res; - - res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM, - "mmss_cc_base"); - if (!res) { - pr_err("%s: Unable to get the MMSS_CC resources", __func__); - return -ENOMEM; - } - - edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res)); - if (!edp_drv->mmss_cc_base) { - pr_err("%s: Unable to remap MMSS_CC resources", __func__); - return -ENOMEM; - } - - return 0; -} - -static void mdss_edp_video_ready(struct mdss_edp_drv_pdata *ep) -{ - pr_debug("%s: edp_video_ready\n", __func__); - complete(&ep->video_comp); -} - -static void mdss_edp_idle_patterns_sent(struct mdss_edp_drv_pdata *ep) -{ - pr_debug("%s: idle_patterns_sent\n", __func__); - complete(&ep->idle_comp); -} - -static void mdss_edp_do_link_train(struct mdss_edp_drv_pdata *ep) -{ - if (ep->cont_splash) - return; - - if (!ep->inited) { - ep->delay_link_train++; - return; - } - - mdss_edp_link_train(ep); -} - -static int edp_event_thread(void *data) -{ - struct mdss_edp_drv_pdata *ep; - unsigned long flag; - u32 todo = 0; - - ep = (struct mdss_edp_drv_pdata *)data; - - while (1) { - wait_event(ep->event_q, (ep->event_pndx != ep->event_gndx)); - spin_lock_irqsave(&ep->event_lock, flag); - if (ep->event_pndx == ep->event_gndx) { - spin_unlock_irqrestore(&ep->event_lock, flag); - break; - } - todo = ep->event_todo_list[ep->event_gndx]; - ep->event_todo_list[ep->event_gndx++] = 0; - ep->event_gndx %= HPD_EVENT_MAX; - spin_unlock_irqrestore(&ep->event_lock, flag); - - pr_debug("%s: todo=%x\n", __func__, todo); - - if (todo == 0) - continue; - - if (todo & EV_EDID_READ) - mdss_edp_edid_read(ep, 0); - - if (todo & EV_DPCD_CAP_READ) - mdss_edp_dpcd_cap_read(ep); - - if (todo & EV_DPCD_STATUS_READ) - mdss_edp_dpcd_status_read(ep); - - if (todo & EV_LINK_TRAIN) - mdss_edp_do_link_train(ep); - - if (todo & EV_VIDEO_READY) - mdss_edp_video_ready(ep); - - if (todo & EV_IDLE_PATTERNS_SENT) - mdss_edp_idle_patterns_sent(ep); - } - - return 0; -} - -static void edp_send_events(struct mdss_edp_drv_pdata *ep, u32 events) -{ - spin_lock(&ep->event_lock); - ep->event_todo_list[ep->event_pndx++] = events; - ep->event_pndx %= HPD_EVENT_MAX; - wake_up(&ep->event_q); - spin_unlock(&ep->event_lock); -} - -irqreturn_t edp_isr(int irq, void *ptr) -{ - struct mdss_edp_drv_pdata *ep = (struct mdss_edp_drv_pdata *)ptr; - unsigned char *base = ep->base; - u32 isr1, isr2, mask1, mask2; - u32 ack; - - spin_lock(&ep->lock); - isr1 = edp_read(base + 0x308); - isr2 = edp_read(base + 0x30c); - - mask1 = isr1 & ep->mask1; - mask2 = isr2 & ep->mask2; - - isr1 &= ~mask1; /* remove masks bit */ - isr2 &= ~mask2; - - pr_debug("%s: isr=%x mask=%x isr2=%x mask2=%x\n", - __func__, isr1, mask1, isr2, mask2); - - ack = isr1 & EDP_INTR_STATUS1; - ack <<= 1; /* ack bits */ - ack |= mask1; - edp_write(base + 0x308, ack); - - ack = isr2 & EDP_INTR_STATUS2; - ack <<= 1; /* ack bits */ - ack |= mask2; - edp_write(base + 0x30c, ack); - spin_unlock(&ep->lock); - - if (isr1 & EDP_INTR_HPD) { - isr1 &= ~EDP_INTR_HPD; /* clear */ - edp_send_events(ep, EV_LINK_TRAIN); - } - - if (isr2 & EDP_INTR_READY_FOR_VIDEO) - edp_send_events(ep, EV_VIDEO_READY); - - if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT) - edp_send_events(ep, EV_IDLE_PATTERNS_SENT); - - if (isr1 && ep->aux_cmd_busy) { - /* clear EDP_AUX_TRANS_CTRL */ - edp_write(base + 0x318, 0); - /* read EDP_INTERRUPT_TRANS_NUM */ - ep->aux_trans_num = edp_read(base + 0x310); - - if (ep->aux_cmd_i2c) - edp_aux_i2c_handler(ep, isr1); - else - edp_aux_native_handler(ep, isr1); - } - - return IRQ_HANDLED; -} - -struct mdss_hw mdss_edp_hw = { - .hw_ndx = MDSS_HW_EDP, - .ptr = NULL, - .irq_handler = edp_isr, -}; - -static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - unsigned long flags; - - spin_lock_irqsave(&edp_drv->lock, flags); - edp_write(edp_drv->base + 0x308, edp_drv->mask1); - edp_write(edp_drv->base + 0x30c, edp_drv->mask2); - spin_unlock_irqrestore(&edp_drv->lock, flags); - - edp_drv->mdss_util->enable_irq(&mdss_edp_hw); -} - -static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - unsigned long flags; - - spin_lock_irqsave(&edp_drv->lock, flags); - edp_write(edp_drv->base + 0x308, 0x0); - edp_write(edp_drv->base + 0x30c, 0x0); - spin_unlock_irqrestore(&edp_drv->lock, flags); - - edp_drv->mdss_util->disable_irq(&mdss_edp_hw); -} - -static int mdss_edp_irq_setup(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_panel_hpd = of_get_named_gpio_flags( - edp_drv->pdev->dev.of_node, "gpio-panel-hpd", 0, - &edp_drv->hpd_flags); - - if (!gpio_is_valid(edp_drv->gpio_panel_hpd)) { - pr_err("%s gpio_panel_hpd %d is not valid ", __func__, - edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_request(edp_drv->gpio_panel_hpd, "edp_hpd_irq_gpio"); - if (ret) { - pr_err("%s unable to request gpio_panel_hpd %d", __func__, - edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_tlmm_config(GPIO_CFG( - edp_drv->gpio_panel_hpd, - 1, - GPIO_CFG_INPUT, - GPIO_CFG_NO_PULL, - GPIO_CFG_2MA), - GPIO_CFG_ENABLE); - if (ret) { - pr_err("%s: unable to config tlmm = %d\n", __func__, - edp_drv->gpio_panel_hpd); - gpio_free(edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_direction_input(edp_drv->gpio_panel_hpd); - if (ret) { - pr_err("%s unable to set direction for gpio_panel_hpd %d", - __func__, edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - mdss_edp_hw.ptr = (void *)(edp_drv); - - if (edp_drv->mdss_util->register_irq(&mdss_edp_hw)) - pr_err("%s: mdss_register_irq failed.\n", __func__); - - - return 0; -} - - -static void mdss_edp_event_setup(struct mdss_edp_drv_pdata *ep) -{ - init_waitqueue_head(&ep->event_q); - spin_lock_init(&ep->event_lock); - - kthread_run(edp_event_thread, (void *)ep, "mdss_edp_hpd"); -} - -static int mdss_edp_probe(struct platform_device *pdev) -{ - int ret; - struct mdss_edp_drv_pdata *edp_drv; - struct mdss_panel_cfg *pan_cfg = NULL; - - if (!mdss_is_ready()) { - pr_err("%s: MDP not probed yet!\n", __func__); - return -EPROBE_DEFER; - } - - pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP); - if (IS_ERR(pan_cfg)) { - return PTR_ERR(pan_cfg); - } else if (!pan_cfg) { - pr_debug("%s: not configured as prim\n", __func__); - return -ENODEV; - } - - if (!pdev->dev.of_node) { - pr_err("%s: Failed\n", __func__); - return -EPERM; - } - - edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL); - if (edp_drv == NULL) { - pr_err("%s: Failed, could not allocate edp_drv", __func__); - return -ENOMEM; - } - - edp_drv->mdss_util = mdss_get_util_intf(); - if (edp_drv->mdss_util == NULL) { - pr_err("Failed to get mdss utility functions\n"); - return -ENODEV; - } - edp_drv->panel_data.panel_info.is_prim_panel = true; - - mdss_edp_hw.irq_info = mdss_intr_line(); - if (mdss_edp_hw.irq_info == NULL) { - pr_err("Failed to get mdss irq information\n"); - return -ENODEV; - } - - edp_drv->pdev = pdev; - edp_drv->pdev->id = 1; - edp_drv->clk_on = 0; - edp_drv->aux_rate = 19200000; - edp_drv->mask1 = EDP_INTR_MASK1; - edp_drv->mask2 = EDP_INTR_MASK2; - mutex_init(&edp_drv->emutex); - spin_lock_init(&edp_drv->lock); - - ret = mdss_edp_get_base_address(edp_drv); - if (ret) - goto probe_err; - - ret = mdss_edp_get_mmss_cc_base_address(edp_drv); - if (ret) - goto edp_base_unmap; - - ret = mdss_edp_regulator_init(edp_drv); - if (ret) - goto mmss_cc_base_unmap; - - ret = mdss_edp_clk_init(edp_drv); - if (ret) - goto edp_clk_deinit; - - ret = mdss_edp_gpio_panel_en(edp_drv); - if (ret) - goto edp_clk_deinit; - - ret = mdss_edp_gpio_lvl_en(edp_drv); - if (ret) - pr_err("%s: No gpio_lvl_en detected\n", __func__); - - ret = mdss_edp_pwm_config(edp_drv); - if (ret) - goto edp_free_gpio_panel_en; - - mdss_edp_irq_setup(edp_drv); - - mdss_edp_aux_init(edp_drv); - - mdss_edp_event_setup(edp_drv); - - edp_drv->cont_splash = edp_drv->mdss_util->panel_intf_status(DISPLAY_1, - MDSS_PANEL_INTF_EDP) ? true : false; - - /* only need aux and ahb clock for aux channel */ - mdss_edp_prepare_aux_clocks(edp_drv); - mdss_edp_aux_clk_enable(edp_drv); - - if (!edp_drv->cont_splash) { - mdss_edp_phy_pll_reset(edp_drv); - mdss_edp_aux_reset(edp_drv); - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_phy_power_ctrl(edp_drv, 1); - mdss_edp_aux_ctrl(edp_drv, 1); - } - - mdss_edp_irq_enable(edp_drv); - - mdss_edp_edid_read(edp_drv, 0); - mdss_edp_dpcd_cap_read(edp_drv); - mdss_edp_fill_link_cfg(edp_drv); - - mdss_edp_irq_disable(edp_drv); - - if (!edp_drv->cont_splash) { - mdss_edp_aux_ctrl(edp_drv, 0); - mdss_edp_phy_power_ctrl(edp_drv, 0); - } - - mdss_edp_aux_clk_disable(edp_drv); - mdss_edp_unprepare_aux_clocks(edp_drv); - - if (edp_drv->cont_splash) { /* vote for clocks */ - mdss_edp_prepare_clocks(edp_drv); - mdss_edp_clk_enable(edp_drv); - } - - mdss_edp_device_register(edp_drv); - - edp_drv->inited = true; - - pr_debug("%s: done\n", __func__); - - return 0; - - -edp_free_gpio_panel_en: - gpio_free(edp_drv->gpio_panel_en); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_free(edp_drv->gpio_lvl_en); -edp_clk_deinit: - mdss_edp_clk_deinit(edp_drv); - mdss_edp_regulator_off(edp_drv); -mmss_cc_base_unmap: - iounmap(edp_drv->mmss_cc_base); -edp_base_unmap: - iounmap(edp_drv->base); -probe_err: - return ret; - -} - -static const struct of_device_id msm_mdss_edp_dt_match[] = { - {.compatible = "qcom,mdss-edp"}, - {} -}; -MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match); - -static struct platform_driver mdss_edp_driver = { - .probe = mdss_edp_probe, - .remove = mdss_edp_remove, - .shutdown = NULL, - .driver = { - .name = "mdss_edp", - .of_match_table = msm_mdss_edp_dt_match, - }, -}; - -static int __init mdss_edp_init(void) -{ - int ret; - - ret = platform_driver_register(&mdss_edp_driver); - if (ret) { - pr_err("%s driver register failed", __func__); - return ret; - } - - return ret; -} -module_init(mdss_edp_init); - -static void __exit mdss_edp_driver_cleanup(void) -{ - platform_driver_unregister(&mdss_edp_driver); -} -module_exit(mdss_edp_driver_cleanup); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("eDP controller driver"); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index d6cd6825e262..da5e7bb8a343 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -678,6 +678,8 @@ struct mdss_ad_info { bool last_calib_valid; u32 ipc_frame_count; u32 bl_data; + u32 bl_min_delta; + u32 bl_low_limit; u32 calc_itr; uint32_t bl_lin[AD_BL_LIN_LEN]; uint32_t bl_lin_inv[AD_BL_LIN_LEN]; @@ -1733,6 +1735,8 @@ void mdss_mdp_hist_intr_done(u32 isr); int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, struct mdss_ad_init_cfg *init_cfg); +int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd, + struct mdss_ad_bl_cfg *ad_bl_cfg); int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, struct mdss_ad_input *input, int wait); int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index cd8df78bc8c0..3fc6d94393d5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -45,7 +45,7 @@ static struct { [MDSS_MDP_SSPP_VIG0] = { 0, { 0, 3, 0 }, { 0, 1, 3 } }, [MDSS_MDP_SSPP_VIG1] = { 1, { 3, 3, 0 }, { 2, 1, 3 } }, [MDSS_MDP_SSPP_VIG2] = { 2, { 6, 3, 0 }, { 4, 1, 3 } }, - [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 4, 1, 3 } }, + [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 6, 1, 3 } }, [MDSS_MDP_SSPP_RGB0] = { 3, { 9, 3, 0 }, { 8, 1, 3 } }, [MDSS_MDP_SSPP_RGB1] = { 4, { 12, 3, 0 }, { 10, 1, 3 } }, [MDSS_MDP_SSPP_RGB2] = { 5, { 15, 3, 0 }, { 12, 1, 3 } }, diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 17cbdd44755a..0ae420724d61 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -114,7 +114,7 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, } static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, - struct mdp_destination_scaler_data *ds_data) + struct mdp_destination_scaler_data *ds_data, int ds_count) { struct mdss_data_type *mdata; struct mdss_panel_info *pinfo; @@ -127,7 +127,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * when we switch between scaling factor or disabling scaling. */ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) { - if (ctl->mixer_left) { + if (ctl->mixer_left && ctl->mixer_left->ds) { /* * Any scale update from usermode, we will update the * mixer width and height with the given LM width and @@ -149,10 +149,25 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, ctl->mixer_left->height = ds_data->lm_height; pr_debug("Update mixer-left width/height: %dx%d\n", ds_data->lm_width, ds_data->lm_width); - } - if (ctl->mixer_right) { + if (ctl->mixer_right && ctl->mixer_right->ds) { + /* + * Advanced to next ds_data structure from commit if + * there is more than 1 for split display usecase. + */ + if (ds_count > 1) + ds_data++; + + pinfo = &ctl->panel_data->panel_info; + if ((ds_data->lm_width > get_panel_xres(pinfo)) || + (ds_data->lm_height > get_panel_yres(pinfo)) || + (ds_data->lm_width == 0) || + (ds_data->lm_height == 0)) { + pr_err("Invalid LM width / height setting\n"); + return -EINVAL; + } + /* * Split display both left and right should have the * same width and height @@ -162,6 +177,15 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, pr_info("Update mixer-right width/height: %dx%d\n", ds_data->lm_width, ds_data->lm_height); + if (ctl->mixer_left && + ((ctl->mixer_right->width != + ctl->mixer_left->width) || + (ctl->mixer_right->height != + ctl->mixer_left->height))) { + pr_err("Mismatch width/heigth in LM for split display\n"); + return -EINVAL; + } + /* * For split display, CTL width should be equal to * whole panel size @@ -2098,17 +2122,17 @@ static int __validate_layers(struct msm_fb_data_type *mfd, if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) && commit->dest_scaler && commit->dest_scaler_cnt) { + struct mdp_destination_scaler_data *ds_data = + commit->dest_scaler; + /* - * Find out which DS block to use based on LM assignment + * Find out which DS block to use based on DS commit info */ - if ((left_cnt > 0) && (right_cnt > 0) && - (commit->dest_scaler_cnt == 2)) + if (commit->dest_scaler_cnt == 2) ds_mode = DS_DUAL_MODE; - else if ((left_cnt > 0) && (right_cnt == 0) && - (commit->dest_scaler_cnt == 1)) + else if (ds_data->dest_scaler_ndx == 0) ds_mode = DS_LEFT; - else if ((left_cnt == 0) && (right_cnt > 0) && - (commit->dest_scaler_cnt == 1)) + else if (ds_data->dest_scaler_ndx == 1) ds_mode = DS_RIGHT; else { pr_err("Commit destination scaler count not matching with LM assignment, DS-cnt:%d\n", @@ -2396,7 +2420,8 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, if (commit->dest_scaler && commit->dest_scaler_cnt) { rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl, - commit->dest_scaler); + commit->dest_scaler, + commit->dest_scaler_cnt); if (IS_ERR_VALUE(rc)) { pr_err("Destination scaler pre-validate failed\n"); return -EINVAL; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 2baa1b9cd8b5..e5cdc750193e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -4109,6 +4109,9 @@ static int mdss_mdp_pp_ioctl(struct msm_fb_data_type *mfd, case mdp_op_ad_cfg: ret = mdss_mdp_ad_config(mfd, &mdp_pp.data.ad_init_cfg); break; + case mdp_op_ad_bl_cfg: + ret = mdss_mdp_ad_bl_config(mfd, &mdp_pp.data.ad_bl_cfg); + break; case mdp_op_ad_input: ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input, 1); if (ret > 0) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index c4cbc220baf8..a760711e7501 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -3073,22 +3073,22 @@ int mdss_mdp_pp_default_overlay_config(struct msm_fb_data_type *mfd, return ret; } -static bool pp_ad_bl_threshold_check(int al_thresh, int base, int prev_bl, +static bool pp_ad_bl_threshold_check(int bl_min_delta, int base, int prev_bl, int curr_bl) { - int bl_thresh = 0, diff = 0; + int bl_delta_factor = 0, diff = 0; bool ret = false; - pr_debug("al_thresh = %d, base = %d\n", al_thresh, base); + pr_debug("bl_min_delta = %d, base = %d\n", bl_min_delta, base); if (base <= 0) { pr_debug("Invalid base for threshold calculation %d\n", base); return ret; } - bl_thresh = (curr_bl * al_thresh) / (base * 4); + bl_delta_factor = (curr_bl * bl_min_delta) / base; diff = (curr_bl > prev_bl) ? (curr_bl - prev_bl) : (prev_bl - curr_bl); - ret = (diff > bl_thresh) ? true : false; - pr_debug("prev_bl =%d, curr_bl = %d, bl_thresh = %d, diff = %d, ret = %d\n", - prev_bl, curr_bl, bl_thresh, diff, ret); + ret = (diff > bl_delta_factor) ? true : false; + pr_debug("prev_bl =%d, curr_bl = %d, bl_delta_factor = %d, diff = %d, ret = %d\n", + prev_bl, curr_bl, bl_delta_factor, diff, ret); return ret; } @@ -3163,7 +3163,10 @@ static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, ad_bl_out = temp; } - if (pp_ad_bl_threshold_check(ad->init.al_thresh, ad->init.alpha_base, + /* update AD backlight based on AD BL low limit */ + ad_bl_out = (ad_bl_out < ad->bl_low_limit ? + ad->bl_low_limit : ad_bl_out); + if (pp_ad_bl_threshold_check(ad->bl_min_delta, ad->init.alpha_base, ad->last_bl, ad_bl_out)) { mfd->ad_bl_level = ad_bl_out; pr_debug("backlight send to AD block: %d\n", mfd->ad_bl_level); @@ -5747,6 +5750,29 @@ ad_config_exit: return ret; } +int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd, + struct mdss_ad_bl_cfg *ad_bl_cfg) +{ + int ret = 0; + struct mdss_ad_info *ad; + + ret = mdss_mdp_get_ad(mfd, &ad); + if (ret == -ENODEV || ret == -EPERM) { + pr_debug("AD not supported on device, disp num %d\n", + mfd->index); + return ret; + } else if (ret || !ad) { + pr_err("Failed to get ad info: ret = %d\n", ret); + return ret; + } + + mutex_lock(&ad->lock); + ad->bl_min_delta = ad_bl_cfg->bl_min_delta; + ad->bl_low_limit = ad_bl_cfg->bl_low_limit; + mutex_unlock(&ad->lock); + return 0; +} + int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, struct mdss_ad_input *input, int wait) { int ret = 0; @@ -6293,6 +6319,8 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) ad->last_calib_valid = false; ad->last_ad_data_valid = false; ad->ipc_frame_count = 0; + ad->bl_min_delta = 0; + ad->bl_low_limit = 0; ad->calc_itr = 0; ad->calc_hw_num = PP_AD_BAD_HW_NUM; memset(&ad->last_calib, 0, sizeof(ad->last_calib)); @@ -6556,6 +6584,8 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets) mdata->ad_cfgs[i].last_str = 0xFFFFFFFF; mdata->ad_cfgs[i].last_bl = 0; mdata->ad_cfgs[i].last_ad_data = 0; + mdata->ad_cfgs[i].bl_low_limit = 0; + mdata->ad_cfgs[i].bl_min_delta = 0; memset(mdata->ad_cfgs[i].last_calib, 0, sizeof(mdata->ad_cfgs[i].last_calib)); mdata->ad_cfgs[i].last_calib_valid = false; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index fafc74d79ac1..1f62232e196b 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -18,7 +18,7 @@ #include <linux/clk/msm-clk.h> #include "mdss_dsi.h" -#include "mdss_edp.h" +#include "mdss_dp.h" #include "mdss_dsi_phy.h" #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_0 0x00 @@ -2423,314 +2423,3 @@ int mdss_dsi_pre_clkon_cb(void *priv, return rc; } - -void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv) -{ - if (edp_drv->aux_clk) - clk_put(edp_drv->aux_clk); - if (edp_drv->pixel_clk) - clk_put(edp_drv->pixel_clk); - if (edp_drv->ahb_clk) - clk_put(edp_drv->ahb_clk); - if (edp_drv->link_clk) - clk_put(edp_drv->link_clk); - if (edp_drv->mdp_core_clk) - clk_put(edp_drv->mdp_core_clk); -} - -int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv) -{ - struct device *dev = &(edp_drv->pdev->dev); - - edp_drv->aux_clk = clk_get(dev, "core_clk"); - if (IS_ERR(edp_drv->aux_clk)) { - pr_err("%s: Can't find aux_clk", __func__); - edp_drv->aux_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->pixel_clk = clk_get(dev, "pixel_clk"); - if (IS_ERR(edp_drv->pixel_clk)) { - pr_err("%s: Can't find pixel_clk", __func__); - edp_drv->pixel_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->ahb_clk = clk_get(dev, "iface_clk"); - if (IS_ERR(edp_drv->ahb_clk)) { - pr_err("%s: Can't find ahb_clk", __func__); - edp_drv->ahb_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->link_clk = clk_get(dev, "link_clk"); - if (IS_ERR(edp_drv->link_clk)) { - pr_err("%s: Can't find link_clk", __func__); - edp_drv->link_clk = NULL; - goto mdss_edp_clk_err; - } - - /* need mdss clock to receive irq */ - edp_drv->mdp_core_clk = clk_get(dev, "mdp_core_clk"); - if (IS_ERR(edp_drv->mdp_core_clk)) { - pr_err("%s: Can't find mdp_core_clk", __func__); - edp_drv->mdp_core_clk = NULL; - goto mdss_edp_clk_err; - } - - return 0; - -mdss_edp_clk_err: - mdss_edp_clk_deinit(edp_drv); - return -EPERM; -} - -int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0) - pr_err("%s: aux_clk - clk_set_rate failed\n", - __func__); - - ret = clk_enable(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to enable aux clk\n", __func__); - goto c2; - } - - ret = clk_enable(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to enable ahb clk\n", __func__); - goto c1; - } - - /* need mdss clock to receive irq */ - ret = clk_enable(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to enable mdp_core_clk\n", __func__); - goto c0; - } - - return 0; -c0: - clk_disable(edp_drv->ahb_clk); -c1: - clk_disable(edp_drv->aux_clk); -c2: - return ret; - -} - -void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_disable(edp_drv->aux_clk); - clk_disable(edp_drv->ahb_clk); - clk_disable(edp_drv->mdp_core_clk); -} - -static void mdss_edp_clk_set_rate(struct mdss_edp_drv_pdata *edp_drv) -{ - if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) - pr_err("%s: link_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) - pr_err("%s: pixel_clk - clk_set_rate failed\n", - __func__); -} - -int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - if (edp_drv->clk_on) { - pr_info("%s: edp clks are already ON\n", __func__); - return 0; - } - - if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) - pr_err("%s: link_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->aux_clk, edp_drv->aux_rate) < 0) - pr_err("%s: aux_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) - pr_err("%s: pixel_clk - clk_set_rate failed\n", - __func__); - - ret = clk_enable(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to enable aux clk\n", __func__); - goto c4; - } - ret = clk_enable(edp_drv->pixel_clk); - if (ret) { - pr_err("%s: Failed to enable pixel clk\n", __func__); - goto c3; - } - ret = clk_enable(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to enable ahb clk\n", __func__); - goto c2; - } - ret = clk_enable(edp_drv->link_clk); - if (ret) { - pr_err("%s: Failed to enable link clk\n", __func__); - goto c1; - } - ret = clk_enable(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to enable mdp_core_clk\n", __func__); - goto c0; - } - - edp_drv->clk_on = 1; - - return 0; - -c0: - clk_disable(edp_drv->link_clk); -c1: - clk_disable(edp_drv->ahb_clk); -c2: - clk_disable(edp_drv->pixel_clk); -c3: - clk_disable(edp_drv->aux_clk); -c4: - return ret; -} - -void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - if (edp_drv->clk_on == 0) { - pr_info("%s: edp clks are already OFF\n", __func__); - return; - } - - clk_disable(edp_drv->aux_clk); - clk_disable(edp_drv->pixel_clk); - clk_disable(edp_drv->ahb_clk); - clk_disable(edp_drv->link_clk); - clk_disable(edp_drv->mdp_core_clk); - - edp_drv->clk_on = 0; -} - -int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - /* ahb clock should be prepared first */ - ret = clk_prepare(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to prepare ahb clk\n", __func__); - goto c3; - } - ret = clk_prepare(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to prepare aux clk\n", __func__); - goto c2; - } - - /* need mdss clock to receive irq */ - ret = clk_prepare(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to prepare mdp_core clk\n", __func__); - goto c1; - } - - return 0; -c1: - clk_unprepare(edp_drv->aux_clk); -c2: - clk_unprepare(edp_drv->ahb_clk); -c3: - return ret; - -} - -void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_unprepare(edp_drv->mdp_core_clk); - clk_unprepare(edp_drv->aux_clk); - clk_unprepare(edp_drv->ahb_clk); -} - -int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - mdss_edp_clk_set_rate(edp_drv); - - /* ahb clock should be prepared first */ - ret = clk_prepare(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to prepare ahb clk\n", __func__); - goto c4; - } - ret = clk_prepare(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to prepare aux clk\n", __func__); - goto c3; - } - ret = clk_prepare(edp_drv->pixel_clk); - if (ret) { - pr_err("%s: Failed to prepare pixel clk\n", __func__); - goto c2; - } - ret = clk_prepare(edp_drv->link_clk); - if (ret) { - pr_err("%s: Failed to prepare link clk\n", __func__); - goto c1; - } - ret = clk_prepare(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to prepare mdp_core clk\n", __func__); - goto c0; - } - - return 0; -c0: - clk_unprepare(edp_drv->link_clk); -c1: - clk_unprepare(edp_drv->pixel_clk); -c2: - clk_unprepare(edp_drv->aux_clk); -c3: - clk_unprepare(edp_drv->ahb_clk); -c4: - return ret; -} - -void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_unprepare(edp_drv->mdp_core_clk); - clk_unprepare(edp_drv->aux_clk); - clk_unprepare(edp_drv->pixel_clk); - clk_unprepare(edp_drv->link_clk); - /* ahb clock should be last one to disable */ - clk_unprepare(edp_drv->ahb_clk); -} - -void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base) -{ - u32 da4, da0, d32c; - u32 dc4, dc0, d330; - - /* pixel clk */ - da0 = edp_read(mmss_cc_base + 0x0a0); - da4 = edp_read(mmss_cc_base + 0x0a4); - d32c = edp_read(mmss_cc_base + 0x32c); - - /* main link clk */ - dc0 = edp_read(mmss_cc_base + 0x0c0); - dc4 = edp_read(mmss_cc_base + 0x0c4); - d330 = edp_read(mmss_cc_base + 0x330); - - pr_err("%s: da0=%x da4=%x d32c=%x dc0=%x dc4=%x d330=%x\n", __func__, - (int)da0, (int)da4, (int)d32c, (int)dc0, (int)dc4, (int)d330); - -} diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h index 47cd73b08a83..28efd55ea8f6 100644 --- a/include/dt-bindings/clock/msm-clocks-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-cobalt.h @@ -241,7 +241,6 @@ #define clk_gcc_usb30_sleep_clk 0xd0b65c92 #define clk_gcc_usb3_phy_aux_clk 0x0d9a36e0 #define clk_gcc_usb3_phy_pipe_clk 0xf279aff2 -#define clk_gcc_usb_phy_cfg_ahb2phy_clk 0xd1231a0e #define clk_gcc_wcss_ahb_s0_clk 0x639a01c4 #define clk_gcc_wcss_axi_m_clk 0xabc48ebd #define clk_gcc_wcss_ecahb_clk 0xf1815ce9 diff --git a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h index 757344602f4a..7ef57256d8f0 100644 --- a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h @@ -213,7 +213,6 @@ #define GCC_USB3_PHY_AUX_CBCR 0x50000 #define GCC_USB3_PHY_PIPE_CBCR 0x50004 #define GCC_USB3PHY_PHY_BCR 0x50024 -#define GCC_USB_PHY_CFG_AHB2PHY_CBCR 0x6A004 #define GCC_WCSS_AHB_S0_CBCR 0x11004 #define GCC_WCSS_AXI_M_CBCR 0x11008 #define GCC_WCSS_ECAHB_CBCR 0x1100C diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h new file mode 100644 index 000000000000..b2fb3c4a56ae --- /dev/null +++ b/include/linux/input/ft5x06_ts.h @@ -0,0 +1,31 @@ +/* + * + * FocalTech ft5x06 TouchScreen driver header file. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_FT5X06_TS_H__ +#define __LINUX_FT5X06_TS_H__ + +struct ft5x06_ts_platform_data { + unsigned long irqflags; + u32 x_max; + u32 y_max; + u32 irq_gpio; + u32 reset_gpio; + int (*power_init) (bool); + int (*power_on) (bool); +}; + +#endif diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h index c3885c72e5ea..0fe0e36c551f 100644 --- a/include/linux/ipa_usb.h +++ b/include/linux/ipa_usb.h @@ -94,7 +94,7 @@ struct ipa_usb_xdci_connect_params { * ipa_usb_xdci_chan_scratch - xDCI protocol SW config area of * channel scratch * - * @last_trb_addr: Address (LSB - based on alignment restrictions) of + * @last_trb_addr_iova: Address (iova LSB - based on alignment restrictions) of * last TRB in queue. Used to identify roll over case * @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation * configuration). Must be aligned to max USB Packet Size. @@ -103,7 +103,7 @@ struct ipa_usb_xdci_connect_params { * @depcmd_hi_addr: Used to generate "Update Transfer" command. */ struct ipa_usb_xdci_chan_scratch { - u16 last_trb_addr; + u16 last_trb_addr_iova; u8 const_buffer_size; u32 depcmd_low_addr; u8 depcmd_hi_addr; @@ -124,6 +124,11 @@ struct ipa_usb_xdci_chan_scratch { * @xfer_ring_base_addr: physical base address of transfer ring. Address must be * aligned to xfer_ring_len rounded to power of two * @xfer_scratch: parameters for xDCI channel scratch + * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr + * @data_buff_base_len: length of data buffer allocated by USB driver + * @data_buff_base_addr: physical base address for the data buffer (where TRBs + * points) + * @data_buff_base_addr_iova: IO virtual address mapped to data_buff_base_addr * */ struct ipa_usb_xdci_chan_params { @@ -140,6 +145,10 @@ struct ipa_usb_xdci_chan_params { u16 xfer_ring_len; u64 xfer_ring_base_addr; struct ipa_usb_xdci_chan_scratch xfer_scratch; + u64 xfer_ring_base_addr_iova; + u32 data_buff_base_len; + u64 data_buff_base_addr; + u64 data_buff_base_addr_iova; }; /** diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h index 8d51ddcd4246..af25f0c01369 100644 --- a/include/linux/qpnp/qpnp-adc.h +++ b/include/linux/qpnp/qpnp-adc.h @@ -242,6 +242,7 @@ enum qpnp_iadc_channels { #define QPNP_ADC_HWMON_NAME_LENGTH 64 #define QPNP_MAX_PROP_NAME_LEN 32 #define QPNP_THERMALNODE_NAME_LENGTH 25 +#define QPNP_ADC_1P25_UV 1250000 /* Structure device for qpnp vadc */ struct qpnp_vadc_chip; @@ -950,6 +951,7 @@ enum qpnp_state_request { * @low_temp: Low temperature threshold for which notification is requested. * @high_thr_voltage: High voltage for which notification is requested. * @low_thr_voltage: Low voltage for which notification is requested. + * @adc_tm_hc: Represents the refreshed BTM register design. * @state_request: Enable/disable the corresponding high and low temperature * thresholds. * @timer_interval1: Select polling rate from qpnp_adc_meas_timer_1 type. @@ -972,6 +974,7 @@ struct qpnp_adc_tm_btm_param { int32_t low_thr; int32_t gain_num; int32_t gain_den; + bool adc_tm_hc; enum qpnp_vadc_channels channel; enum qpnp_state_request state_request; enum qpnp_adc_meas_timer_1 timer_interval; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 89effb61d153..44b6222db9a3 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -176,6 +176,8 @@ struct msm_usb_cable { #define DEVICE_IN_SS_MODE BIT(5) #define PHY_LANE_A BIT(6) #define PHY_LANE_B BIT(7) +#define PHY_HSFS_MODE BIT(8) +#define PHY_LS_MODE BIT(9) #define USB_NUM_BUS_CLOCKS 3 diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d6f6e5006ee9..d18cbafc3455 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -285,11 +285,10 @@ struct xfrm_policy_afinfo { unsigned short family; struct dst_ops *dst_ops; void (*garbage_collect)(struct net *net); - struct dst_entry *(*dst_lookup)(struct net *net, - int tos, int oif, + struct dst_entry *(*dst_lookup)(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr); - int (*get_saddr)(struct net *net, int oif, + int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr); void (*decode_session)(struct sk_buff *skb, diff --git a/include/soc/qcom/service-locator.h b/include/soc/qcom/service-locator.h index be1a2b431dd9..6bf8ac0be15f 100644 --- a/include/soc/qcom/service-locator.h +++ b/include/soc/qcom/service-locator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -51,16 +51,22 @@ struct pd_qmi_client_data { struct servreg_loc_entry_v01 *domain_list; }; +enum service_locator_state { + LOCATOR_DOWN = 0x0F, + LOCATOR_UP = 0x1F, +}; + #if defined(CONFIG_MSM_SERVICE_LOCATOR) /* - * Use this api to request information regarding the process domains on which - * a particular service runs. The client name and the service name inside the - * pd_qmi_client_data structure need to be filled in by the client calling the - * api. The total domains, db revision and the domain list will be filled in + * Use this api to request information regarding the process domains on + * which a particular service runs. The client name, the service name + * and notifier block pointer need to be provided by client calling the api. + * The total domains, db revision and the domain list will be filled in * by the service locator. * Returns 0 on success; otherwise a value < 0 if no valid subsystem is found. */ -int get_service_location(struct pd_qmi_client_data *data); +int get_service_location(char *client_name, char *service_name, + struct notifier_block *locator_nb); /* * Use this api to request information regarding the subsystem the process @@ -73,9 +79,10 @@ int find_subsys(const char *pd_path, char *subsys); #else -static inline int get_service_location(struct pd_qmi_client_data *data) +static inline int get_service_location(char *client_name, + char *service_name, struct notifier_block *locator_nb); { - return 0; + return -ENODEV; } static inline int find_subsys(const char *pd_path, const char *subsys) diff --git a/include/sound/cpe_cmi.h b/include/sound/cpe_cmi.h index 5ce52ae912d4..cbf83e7a7e91 100644 --- a/include/sound/cpe_cmi.h +++ b/include/sound/cpe_cmi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,7 @@ #include <linux/types.h> #define CPE_AFE_PORT_1_TX 1 +#define CPE_AFE_PORT_3_TX 3 #define CPE_AFE_PORT_ID_2_OUT 0x02 #define CMI_INBAND_MESSAGE_SIZE 127 @@ -80,6 +81,7 @@ #define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A #define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14) #define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15) +#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E) /* AFE Service command opcodes */ #define CPE_AFE_PORT_CMD_START (0x1001) @@ -125,7 +127,7 @@ enum { CMI_CPE_SERVICE_ID_MAX, }; -#define CPE_LSM_SESSION_ID_MAX 1 +#define CPE_LSM_SESSION_ID_MAX 2 #define IS_VALID_SESSION_ID(s_id) \ (s_id <= CPE_LSM_SESSION_ID_MAX) @@ -418,6 +420,15 @@ struct cpe_lsm_lab_latency_config { struct cpe_lsm_lab_config latency_cfg; } __packed; +struct cpe_lsm_media_fmt_param { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 sample_rate; + u16 num_channels; + u16 bit_width; +} __packed; + #define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\ sizeof(struct cpe_lsm_lab_latency_config) - \ @@ -474,4 +485,8 @@ struct cpe_lsm_lab_latency_config { sizeof(struct cmi_hdr)) #define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \ sizeof(struct cpe_param_data)) +#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \ + sizeof(struct cmi_hdr)) +#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) #endif /* __CPE_CMI_H__ */ diff --git a/include/sound/cpe_core.h b/include/sound/cpe_core.h index 289ed1df34f5..323a63fd6238 100644 --- a/include/sound/cpe_core.h +++ b/include/sound/cpe_core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,12 @@ struct lsm_out_fmt_cfg { u8 transfer_mode; }; +struct lsm_hw_params { + u32 sample_rate; + u16 num_chs; + u16 bit_width; +}; + struct cpe_lsm_session { /* sound model related */ void *snd_model_data; @@ -160,6 +166,11 @@ struct wcd_cpe_lsm_ops { void (*lsm_get_snd_model_offset) (void *core_handle, struct cpe_lsm_session *, size_t *offset); + int (*lsm_set_media_fmt_params)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param); + int (*lsm_set_port)(void *core_handle, + struct cpe_lsm_session *session, void *data); }; int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *); diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h index dbba773cd49d..a80278954a77 100644 --- a/include/uapi/linux/msm_kgsl.h +++ b/include/uapi/linux/msm_kgsl.h @@ -308,6 +308,7 @@ enum kgsl_timestamp_type { #define KGSL_PROP_GPMU_VERSION 0x16 #define KGSL_PROP_HIGHEST_BANK_BIT 0x17 #define KGSL_PROP_DEVICE_BITNESS 0x18 +#define KGSL_PROP_DEVICE_QDSS_STM 0x19 struct kgsl_shadowprop { unsigned long gpuaddr; @@ -315,6 +316,11 @@ struct kgsl_shadowprop { unsigned int flags; /* contains KGSL_FLAGS_ values */ }; +struct kgsl_qdss_stm_prop { + uint64_t gpuaddr; + uint64_t size; +}; + struct kgsl_version { unsigned int drv_major; unsigned int drv_minor; diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index a1237ed996a5..1df65c7f90b3 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -1176,6 +1176,11 @@ struct mdss_ad_cfg { uint32_t bl_ctrl_mode; }; +struct mdss_ad_bl_cfg { + uint32_t bl_min_delta; + uint32_t bl_low_limit; +}; + /* ops uses standard MDP_PP_* flags */ struct mdss_ad_init_cfg { uint32_t ops; @@ -1220,11 +1225,14 @@ enum { mdp_op_calib_dcm_state, mdp_op_max, mdp_op_pa_dither_cfg, + mdp_op_ad_bl_cfg, mdp_op_pp_max = 255, }; #define mdp_op_pa_dither_cfg mdp_op_pa_dither_cfg #define mdp_op_pp_max mdp_op_pp_max +#define mdp_op_ad_bl_cfg mdp_op_ad_bl_cfg + enum { WB_FORMAT_NV12, WB_FORMAT_RGB_565, @@ -1254,6 +1262,7 @@ struct msmfb_mdp_pp { struct mdss_ad_input ad_input; struct mdp_calib_config_buffer calib_buffer; struct mdp_calib_dcm_state calib_dcm; + struct mdss_ad_bl_cfg ad_bl_cfg; } data; }; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 27fe13a534b4..fe19c7596f8c 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -1166,6 +1166,13 @@ enum v4l2_mpeg_vidc_video_lowlatency_mode { #define V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 92) +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 93) +enum v4l2_mpeg_vidc_video_h264_transform_8x8 { + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1, +}; + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h index e658f4b56df9..93613855228e 100644 --- a/include/uapi/media/msmb_isp.h +++ b/include/uapi/media/msmb_isp.h @@ -6,6 +6,7 @@ #define MAX_PLANES_PER_STREAM 3 #define MAX_NUM_STREAM 7 +#define ISP_VERSION_48 48 #define ISP_VERSION_47 47 #define ISP_VERSION_46 46 #define ISP_VERSION_44 44 diff --git a/net/activity_stats.c b/net/activity_stats.c index 3bf92d80b8b9..8a3e93470069 100644 --- a/net/activity_stats.c +++ b/net/activity_stats.c @@ -15,7 +15,6 @@ */ #include <linux/proc_fs.h> -#include <linux/seq_file.h> #include <linux/suspend.h> #include <net/net_namespace.h> @@ -60,19 +59,29 @@ void activity_stats_update(void) spin_unlock_irqrestore(&activity_lock, flags); } -static int activity_stats_show(struct seq_file *m, void *v) +static int activity_stats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { int i; + int len; + char *p = page; - seq_printf(m, "Min Bucket(sec) Count\n"); + /* Only print if offset is 0, or we have enough buffer space */ + if (off || count < (30 * BUCKET_MAX + 22)) + return -ENOMEM; + + len = snprintf(p, count, "Min Bucket(sec) Count\n"); + count -= len; + p += len; for (i = 0; i < BUCKET_MAX; i++) { - seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); - if (seq_has_overflowed(m)) - return -ENOSPC; + len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); + count -= len; + p += len; } + *eof = 1; - return 0; + return p - page; } static int activity_stats_notifier(struct notifier_block *nb, @@ -91,26 +100,14 @@ static int activity_stats_notifier(struct notifier_block *nb, return 0; } -static int activity_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, activity_stats_show, PDE_DATA(inode)); -} - -static const struct file_operations activity_stats_fops = { - .open = activity_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - static struct notifier_block activity_stats_notifier_block = { .notifier_call = activity_stats_notifier, }; static int __init activity_stats_init(void) { - proc_create("activity", S_IRUGO, - init_net.proc_net_stat, &activity_stats_fops); + create_proc_read_entry("activity", S_IRUGO, + init_net.proc_net_stat, activity_stats_read_proc, NULL); return register_pm_notifier(&activity_stats_notifier_block); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 7b0edb37a115..ab7ab839b057 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -20,7 +20,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo; static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, - int tos, int oif, + int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { @@ -29,7 +29,6 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, memset(fl4, 0, sizeof(*fl4)); fl4->daddr = daddr->a4; fl4->flowi4_tos = tos; - fl4->flowi4_oif = oif; if (saddr) fl4->saddr = saddr->a4; @@ -42,22 +41,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, return ERR_CAST(rt); } -static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif, +static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { struct flowi4 fl4; - return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr); + return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr); } -static int xfrm4_get_saddr(struct net *net, int oif, +static int xfrm4_get_saddr(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr) { struct dst_entry *dst; struct flowi4 fl4; - dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr); + dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 31144c486c52..c2b523c498d8 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -891,8 +891,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, #ifdef CONFIG_IPV6_SUBTREES ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || #endif - (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) && - (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) { + (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { dst_release(dst); dst = NULL; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index dd76806358fe..01d7ee57d937 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1049,9 +1049,6 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn; - if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) - oif = 0; - redo_rt6_select: rt = rt6_select(fn, oif, strict); if (rt->rt6i_nsiblings) diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c074771a10f7..c23742462f02 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -27,7 +27,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo; -static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, +static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { @@ -36,8 +36,6 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, int err; memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_oif = oif; - fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); @@ -53,13 +51,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, return dst; } -static int xfrm6_get_saddr(struct net *net, int oif, +static int xfrm6_get_saddr(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr) { struct dst_entry *dst; struct net_device *dev; - dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr); + dst = xfrm6_dst_lookup(net, 0, NULL, daddr); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b5e665b3cfb0..cf0193b74ae3 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -115,8 +115,7 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) rcu_read_unlock(); } -static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, - int tos, int oif, +static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr, int family) @@ -128,15 +127,14 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, if (unlikely(afinfo == NULL)) return ERR_PTR(-EAFNOSUPPORT); - dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr); + dst = afinfo->dst_lookup(net, tos, saddr, daddr); xfrm_policy_put_afinfo(afinfo); return dst; } -static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, - int tos, int oif, +static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, int family) @@ -155,7 +153,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, daddr = x->coaddr; } - dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family); + dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family); if (!IS_ERR(dst)) { if (prev_saddr != saddr) @@ -1395,15 +1393,15 @@ int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) } static int -xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local, - xfrm_address_t *remote, unsigned short family) +xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote, + unsigned short family) { int err; struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EINVAL; - err = afinfo->get_saddr(net, oif, local, remote); + err = afinfo->get_saddr(net, local, remote); xfrm_policy_put_afinfo(afinfo); return err; } @@ -1432,8 +1430,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, remote = &tmpl->id.daddr; local = &tmpl->saddr; if (xfrm_addr_any(local, tmpl->encap_family)) { - error = xfrm_get_saddr(net, fl->flowi_oif, - &tmp, remote, + error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family); if (error) goto fail; @@ -1712,8 +1709,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { family = xfrm[i]->props.family; - dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif, - &saddr, &daddr, family); + dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr, + family); err = PTR_ERR(dst); if (IS_ERR(dst)) goto put_states; diff --git a/sound/core/control.c b/sound/core/control.c index a85d45595d02..b4fe9b002512 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, if (snd_BUG_ON(!card || !id)) return; + if (card->shutdown) + return; read_lock(&card->ctl_files_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 019751a83e25..9e4743e833be 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -849,6 +849,22 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, } EXPORT_SYMBOL(snd_pcm_new_internal); +static void free_pcm_kctl(struct snd_pcm_str *pstr) +{ + if (pstr->chmap_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + pstr->chmap_kctl = NULL; + } + if (pstr->vol_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl); + pstr->vol_kctl = NULL; + } + if (pstr->usr_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->usr_kctl); + pstr->usr_kctl = NULL; + } +} + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; @@ -871,6 +887,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) kfree(setup); } #endif + free_pcm_kctl(pstr); if (pstr->substream_count) put_device(&pstr->dev); } @@ -1135,18 +1152,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) for (cidx = 0; cidx < 2; cidx++) { if (!pcm->internal) snd_unregister_device(&pcm->streams[cidx].dev); - if (pcm->streams[cidx].chmap_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); - pcm->streams[cidx].chmap_kctl = NULL; - } - if (pcm->streams[cidx].vol_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].vol_kctl); - pcm->streams[cidx].vol_kctl = NULL; - } - if (pcm->streams[cidx].usr_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].usr_kctl); - pcm->streams[cidx].usr_kctl = NULL; - } + free_pcm_kctl(&pcm->streams[cidx]); } mutex_unlock(&pcm->open_mutex); mutex_unlock(®ister_mutex); diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index b1e105b3153a..0b4bd3c15127 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -35,9 +35,10 @@ #include "wcd_cmi_api.h" #define CMI_CMD_TIMEOUT (10 * HZ) -#define WCD_CPE_LSM_MAX_SESSIONS 1 +#define WCD_CPE_LSM_MAX_SESSIONS 2 #define WCD_CPE_AFE_MAX_PORTS 4 #define AFE_SVC_EXPLICIT_PORT_START 1 +#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */ #define ELF_FLAG_EXECUTE (1 << 0) #define ELF_FLAG_WRITE (1 << 1) @@ -1638,7 +1639,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, core->cpe_users); if (enable) { - if (core->cpe_users == 0) { + core->cpe_users++; + if (core->cpe_users == 1) { ret = wcd_cpe_enable(core, enable); if (ret) { dev_err(core->dev, @@ -1646,7 +1648,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, __func__, ret); goto done; } - core->cpe_users++; } else { dev_dbg(core->dev, "%s: cpe already enabled, users = %u\n", @@ -1654,7 +1655,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, goto done; } } else { - if (core->cpe_users == 1) { + core->cpe_users--; + if (core->cpe_users == 0) { ret = wcd_cpe_enable(core, enable); if (ret) { dev_err(core->dev, @@ -1662,7 +1664,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, __func__, ret); goto done; } - core->cpe_users--; } else { dev_dbg(core->dev, "%s: %u valid users on cpe\n", @@ -3335,7 +3336,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle, { struct cmi_hdr cmd_lsm_start; struct wcd_cpe_core *core = core_handle; - struct cpe_lsm_ids ids; int ret = 0; ret = wcd_cpe_is_valid_lsm_session(core, session, @@ -3343,30 +3343,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle, if (ret) return ret; - /* Send connect to port (input) */ - ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; - ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT; - ret = wcd_cpe_send_param_connectport(core, session, - NULL, &ids, CPE_AFE_PORT_1_TX); - if (ret) { - dev_err(core->dev, - "%s: Failed to set connectPort, err=%d\n", - __func__, ret); - return ret; - } - - /* Send connect to port (output) */ - ids.module_id = CPE_LSM_MODULE_FRAMEWORK; - ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT; - ret = wcd_cpe_send_param_connectport(core, session, - NULL, &ids, session->afe_out_port_id); - if (ret) { - dev_err(core->dev, - "%s: Failed to set connectPort, err=%d\n", - __func__, ret); - return ret; - } - WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr)); @@ -3449,9 +3425,12 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( if (!wcd_cpe_lsm_session_active()) afe_register_service = true; - for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) - if (!lsm_sessions[i]) + for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) { + if (!lsm_sessions[i]) { session_id = i; + break; + } + } if (session_id < 0) { dev_err(core->dev, @@ -3875,6 +3854,83 @@ static void wcd_cpe_snd_model_offset(void *core_handle, *offset = sizeof(struct cpe_param_data); } +static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param) +{ + struct cpe_lsm_media_fmt_param media_fmt; + struct cmi_hdr *msg_hdr = &media_fmt.hdr; + struct wcd_cpe_core *core = core_handle; + struct cpe_param_data *param_d = &media_fmt.param; + struct cpe_lsm_ids ids; + int ret; + + memset(&media_fmt, 0, sizeof(media_fmt)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_MEDIA_FMT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + ret = -EINVAL; + goto done; + } + + memset(&ids, 0, sizeof(ids)); + ids.module_id = CPE_LSM_MODULE_FRAMEWORK; + ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT; + + wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + media_fmt.minor_version = 1; + media_fmt.sample_rate = param->sample_rate; + media_fmt.num_channels = param->num_chs; + media_fmt.bit_width = param->bit_width; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt); + if (ret) + dev_err(core->dev, + "%s: Set_param(media_format) failed, err=%d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static int wcd_cpe_lsm_set_port(void *core_handle, + struct cpe_lsm_session *session, void *data) +{ + u32 port_id; + int ret; + struct cpe_lsm_ids ids; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + if (!data) { + dev_err(core->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + port_id = *(u32 *)data; + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id); + + memset(&ids, 0, sizeof(ids)); + ids.module_id = LSM_MODULE_ID_FRAMEWORK; + ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + ret = wcd_cpe_send_param_connectport(core, session, NULL, + &ids, port_id); + if (ret) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, ret); +done: + return ret; +} + /* * wcd_cpe_get_lsm_ops: register lsm driver to codec * @lsm_ops: structure with lsm callbacks @@ -3899,6 +3955,9 @@ int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg; lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param; lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset; + lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params; + lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port; + return 0; } EXPORT_SYMBOL(wcd_cpe_get_lsm_ops); @@ -4129,10 +4188,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle, goto rel_cal_mutex; } - rc = fill_cmi_header(hdr, port_d->port_id, - CMI_CPE_AFE_SERVICE_ID, - 0, 20, CPE_AFE_CMD_SET_PARAM, - true); + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + true); if (rc) { dev_err(core->dev, "%s: invalid params for header, err = %d\n", @@ -4163,10 +4222,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle, hdr = (struct cmi_hdr *) inb_msg; - rc = fill_cmi_header(hdr, port_d->port_id, - CMI_CPE_AFE_SERVICE_ID, - 0, afe_cal->cal_data.size, - CPE_AFE_CMD_SET_PARAM, false); + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + false); if (rc) { dev_err(core->dev, "%s: invalid params for header, err = %d\n", @@ -4299,8 +4358,12 @@ static int wcd_cpe_afe_cmd_port_cfg(void *core_handle, port_cfg_cmd.bit_width = afe_cfg->bit_width; port_cfg_cmd.num_channels = afe_cfg->num_channels; port_cfg_cmd.sample_rate = afe_cfg->sample_rate; - port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, - afe_cfg->sample_rate); + + if (afe_port_d->port_id == CPE_AFE_PORT_3_TX) + port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE; + else + port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, + afe_cfg->sample_rate); ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd); if (ret) diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c index c4ea4ed857ca..60d09dfaeb7f 100644 --- a/sound/soc/msm/qdsp6v2/audio_calibration.c +++ b/sound/soc/msm/qdsp6v2/audio_calibration.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -490,7 +490,13 @@ static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, goto unlock; if (data == NULL) goto unlock; - if (copy_to_user((void *)arg, data, + if ((sizeof(data->hdr) + data->hdr.cal_type_size) > size) { + pr_err("%s: header size %zd plus cal type size %d are greater than data buffer size %d\n", + __func__, sizeof(data->hdr), + data->hdr.cal_type_size, size); + ret = -EFAULT; + goto unlock; + } else if (copy_to_user((void *)arg, data, sizeof(data->hdr) + data->hdr.cal_type_size)) { pr_err("%s: Could not copy cal type to user\n", __func__); diff --git a/sound/usb/card.c b/sound/usb/card.c index e94f4d2f2620..524688e4c144 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -643,6 +643,7 @@ static int usb_audio_probe(struct usb_interface *intf, usb_chip[chip->index] = chip; chip->num_interfaces++; usb_set_intfdata(intf, chip); + usb_enable_autosuspend(chip->dev); atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; |
