diff options
24 files changed, 1664 insertions, 575 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt index 0b65b776645b..85656e312acc 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -24,6 +24,9 @@ Required properties - clocks: List of Phandles for clock device nodes needed by the device. - clock-names: List of clock names needed by the device. +- qcom,aux-en-gpio: Specifies the aux-channel enable gpio. +- qcom,aux-sel-gpio: Specifies the aux-channel select gpio. +- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio. Optional properties: - qcom,<type>-supply-entries: A node that lists the elements of the supply used by the @@ -42,6 +45,12 @@ Optional properties: -- 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 +- qcom,hpd-gpio: Specifies the HPD gpio. +- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node + Refer to pinctrl-bindings.txt +- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin + controller. These pin configurations are installed in the pinctrl + device node. Refer to pinctrl-bindings.txt Example: mdss_dp_ctrl: qcom,dp_ctrl@c990000 { @@ -115,5 +124,15 @@ Example: qcom,supply-disable-load = <32>; }; }; + + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active + &mdss_dp_hpd_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend + &mdss_dp_hpd_suspend>; + qcom,aux-en-gpio = <&tlmm 77 0>; + qcom,aux-sel-gpio = <&tlmm 78 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; + qcom,hpd-gpio = <&tlmm 34 0>; }; diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt index 222b4fe66697..e09f12737ed9 100644 --- a/Documentation/devicetree/bindings/platform/msm/ipa.txt +++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt @@ -119,6 +119,9 @@ IPA SMMU sub nodes - qcom,iova-mapping: specifies the start address and size of iova space. +- qcom,additional-mapping: specifies any addtional mapping needed for this + context bank. The format is <iova pa size> + IPA SMP2P sub nodes -compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from @@ -195,7 +198,10 @@ qcom,ipa@fd4c0000 { ipa_smmu_ap: ipa_smmu_ap { compatible = "qcom,ipa-smmu-ap-cb"; iommus = <&anoc2_smmu 0x30>; - qcom,iova-mapping = <0x10000000 0x40000000>; + qcom,iova-mapping = <0x20000000 0x40000000>; + qcom,additional-mapping = + /* modem tables in IMEM */ + <0x146BD000 0x146BD000 0x2000>; }; ipa_smmu_wlan: ipa_smmu_wlan { diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index a38104faf261..3136687adb57 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -6,6 +6,10 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties Required properties: - compatible: must be "snps,dwc3" - reg : Address and length of the register set for the device + Required regs are: + - "core_base" : USB DWC3 controller register set. + - "ahb2phy_base" : AHB2PHY register base. It is used to update read/write + wait cycle for accessing PHY. - interrupts: Interrupts used by the dwc3 controller. Optional properties: @@ -61,7 +65,10 @@ This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { compatible = "snps,dwc3"; - reg = <0x4a030000 0xcfff>; + reg = <0x07600000 0xfc000>, + <0x7416000 0x400>; + reg-names = "core_base", + "ahb2phy_base"; interrupts = <0 92 4> usb-phy = <&usb2_phy>, <&usb3,phy>; tx-fifo-resize; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index 2f82fbfda14f..dd9c13b4b5ff 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -180,16 +180,11 @@ Required properties: - phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode. Optional properties: - - reg: Address and length register set to control QUSB2 PHY - "qscratch_base" : QSCRATCH base register set. + - reg-names: Additional registers corresponding with the following: "tune2_efuse_addr": EFUSE based register address to read TUNE2 parameter. via the QSCRATCH interface. "emu_phy_base" : phy base address used for programming emulation target phy. "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset. - - reg-names: Should be "qscratch_base". The qscratch register bank - 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" @@ -210,10 +205,8 @@ Optional properties: Example: qusb_phy: qusb@f9b39000 { compatible = "qcom,qusb2phy"; - reg = <0x00079000 0x7000>, - <0x08af8800 0x400>; - reg-names = "qusb_phy_base", - "qscratch_base"; + reg = <0x00079000 0x7000>; + reg-names = "qusb_phy_base"; vdd-supply = <&pm8994_s2_corner>; vdda18-supply = <&pm8994_l6>; vdda33-supply = <&pm8994_l24>; 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.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 7a26b9f0b227..9bcc375e275c 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -2059,11 +2059,9 @@ qusb_phy0: qusb@7411000 { compatible = "qcom,qusb2phy"; reg = <0x07411000 0x180>, - <0x06af8800 0x400>, <0x0007024c 0x4>, <0x00388018 0x4>; reg-names = "qusb_phy_base", - "qscratch_base", "tune2_efuse_addr", "ref_clk_addr"; vdd-supply = <&pm8994_s2_corner>; @@ -2096,11 +2094,9 @@ qusb_phy1: qusb@7412000 { compatible = "qcom,qusb2phy"; reg = <0x07412000 0x180>, - <0x076f8800 0x400>, <0x0007024c 0x4>, <0x00388014 0x4>; reg-names = "qusb_phy_base", - "qscratch_base", "tune2_efuse_addr", "ref_clk_addr"; vdd-supply = <&pm8994_s2_corner>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index f02abd2173ef..aa9390de6525 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -1779,10 +1779,8 @@ qusb_phy0: qusb@c012000 { compatible = "qcom,qusb2phy-v2"; - reg = <0x0c012000 0x2a8>, - <0x0a8f8800 0x400>; - reg-names = "qusb_phy_base", - "qscratch_base"; + reg = <0x0c012000 0x2a8>; + reg-names = "qusb_phy_base"; vdd-supply = <&pmcobalt_l1>; vdda18-supply = <&pmcobalt_l12>; vdda33-supply = <&pmcobalt_l24>; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index c553be1ad717..3dd9738f67c7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -37,6 +37,7 @@ #include <linux/hashtable.h> #include <linux/hash.h> #include <soc/qcom/subsystem_restart.h> +#include <soc/qcom/smem.h> #define IPA_SUBSYSTEM_NAME "ipa_fws" #include "ipa_i.h" #include "../ipa_rm_i.h" @@ -75,6 +76,17 @@ #define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2 #define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3 +#define IPA_SMEM_SIZE (8 * 1024) + +/* round addresses for closes page per SMMU requirements */ +#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \ + do { \ + (iova_p) = rounddown((iova), PAGE_SIZE); \ + (pa_p) = rounddown((pa), PAGE_SIZE); \ + (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \ + } while (0) + + /* The relative location in /lib/firmware where the FWs will reside */ #define IPA_FWS_PATH "ipa/ipa_fws.elf" @@ -4813,6 +4825,10 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) int fast = 1; int bypass = 1; u32 iova_ap_mapping[2]; + u32 add_map_size; + const u32 *add_map; + void *smem_addr; + int i; IPADBG("AP CB probe: sub pdev=%p\n", dev); @@ -4902,6 +4918,55 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) return result; } + add_map = of_get_property(dev->of_node, + "qcom,additional-mapping", &add_map_size); + if (add_map) { + /* mapping size is an array of 3-tuple of u32 */ + if (add_map_size % (3 * sizeof(u32))) { + IPAERR("wrong additional mapping format\n"); + cb->valid = false; + return -EFAULT; + } + + /* iterate of each entry of the additional mapping array */ + for (i = 0; i < add_map_size / sizeof(u32); i += 3) { + u32 iova = be32_to_cpu(add_map[i]); + u32 pa = be32_to_cpu(add_map[i + 1]); + u32 size = be32_to_cpu(add_map[i + 2]); + unsigned long iova_p; + phys_addr_t pa_p; + u32 size_p; + + IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, + iova_p, pa_p, size_p); + IPADBG("mapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + ipa3_iommu_map(cb->mapping->domain, + iova_p, pa_p, size_p, + IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } + } + + /* map SMEM memory for IPA table accesses */ + smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, IPA_SMEM_SIZE, + SMEM_MODEM, 0); + if (smem_addr) { + phys_addr_t iova = smem_virt_to_phys(smem_addr); + phys_addr_t pa = iova; + unsigned long iova_p; + phys_addr_t pa_p; + u32 size_p; + + IPA_SMMU_ROUND_TO_PAGE(iova, pa, IPA_SMEM_SIZE, + iova_p, pa_p, size_p); + IPADBG("mapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + ipa3_iommu_map(cb->mapping->domain, + iova_p, pa_p, size_p, + IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } + + smmu_info.present = true; if (!ipa3_bus_scale_table) 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/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 4635edf0189b..4d35de1c14c5 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -92,6 +92,13 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu"); #define PIPE3_PHYSTATUS_SW BIT(3) #define PIPE_UTMI_CLK_DIS BIT(8) +#define HS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x10) +#define UTMI_OTG_VBUS_VALID BIT(20) +#define SW_SESSVLD_SEL BIT(28) + +#define SS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x30) +#define LANE0_PWR_PRESENT BIT(24) + /* GSI related registers */ #define GSI_TRB_ADDR_BIT_53_MASK (1 << 21) #define GSI_TRB_ADDR_BIT_55_MASK (1 << 23) @@ -3090,6 +3097,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) return 0; } +static void dwc3_override_vbus_status(struct dwc3_msm *mdwc, bool vbus_present) +{ + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + /* Update OTG VBUS Valid from HSPHY to controller */ + dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, + vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : + UTMI_OTG_VBUS_VALID, + vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : 0); + + /* Update only if Super Speed is supported */ + if (dwc->maximum_speed == USB_SPEED_SUPER) { + /* Update VBUS Valid from SSPHY to controller */ + dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, + LANE0_PWR_PRESENT, + vbus_present ? LANE0_PWR_PRESENT : 0); + } +} + /** * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. * @@ -3110,6 +3136,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) dev_dbg(mdwc->dev, "%s: turn on gadget %s\n", __func__, dwc->gadget.name); + dwc3_override_vbus_status(mdwc, true); usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER); @@ -3125,6 +3152,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) usb_gadget_vbus_disconnect(&dwc->gadget); usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); + dwc3_override_vbus_status(mdwc, false); dwc3_usb3_phy_suspend(dwc, false); } diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index 5a29fc96f940..dd8149ef097d 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -54,10 +54,6 @@ #define QUSB2PHY_PORT_TUNE2 0x240 -#define HS_PHY_CTRL_REG 0x10 -#define UTMI_OTG_VBUS_VALID BIT(20) -#define SW_SESSVLD_SEL BIT(28) - #define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ #define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ #define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */ @@ -76,7 +72,6 @@ MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); struct qusb_phy { struct usb_phy phy; void __iomem *base; - void __iomem *qscratch_base; void __iomem *tune2_efuse_reg; struct clk *ref_clk_src; @@ -625,25 +620,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) return 0; } -static void qusb_write_readback(void *base, u32 offset, - const u32 mask, u32 val) -{ - u32 write_val, tmp = readl_relaxed(base + offset); - - tmp &= ~mask; /* retain other bits */ - write_val = tmp | val; - - writel_relaxed(write_val, base + offset); - - /* Read back to see if val was written */ - tmp = readl_relaxed(base + offset); - tmp &= mask; /* clear other bits */ - - if (tmp != val) - pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", - __func__, val, offset); -} - static int qusb_phy_notify_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -651,21 +627,11 @@ static int qusb_phy_notify_connect(struct usb_phy *phy, qphy->cable_connected = true; - 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, - UTMI_OTG_VBUS_VALID); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, SW_SESSVLD_SEL); - - dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -676,17 +642,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy, qphy->cable_connected = false; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, 0); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, 0); - - dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -768,16 +725,6 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "qscratch_base"); - if (res) { - qphy->qscratch_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qphy->qscratch_base)) { - dev_dbg(dev, "couldn't ioremap qscratch_base\n"); - qphy->qscratch_base = NULL; - } - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "emu_phy_base"); if (res) { qphy->emu_phy_base = devm_ioremap_resource(dev, res); @@ -977,11 +924,8 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.set_suspend = qusb_phy_set_suspend; qphy->phy.shutdown = qusb_phy_shutdown; qphy->phy.type = USB_PHY_TYPE_USB2; - - if (qphy->qscratch_base) { - qphy->phy.notify_connect = qusb_phy_notify_connect; - qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; - } + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; ret = usb_add_phy_dev(&qphy->phy); if (ret) diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 1eb0c8d6b62f..325f5fcf161b 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -88,9 +88,6 @@ #define LINESTATE_DP BIT(0) #define LINESTATE_DM BIT(1) -#define HS_PHY_CTRL_REG 0x10 -#define UTMI_OTG_VBUS_VALID BIT(20) -#define SW_SESSVLD_SEL BIT(28) #define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ #define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ @@ -109,7 +106,6 @@ MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2"); struct qusb_phy { struct usb_phy phy; void __iomem *base; - void __iomem *qscratch_base; void __iomem *tune2_efuse_reg; void __iomem *ref_clk_base; @@ -686,24 +682,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) return 0; } -static void qusb_write_readback(void *base, u32 offset, - const u32 mask, u32 val) -{ - u32 write_val, tmp = readl_relaxed(base + offset); - tmp &= ~mask; /* retain other bits */ - write_val = tmp | val; - - writel_relaxed(write_val, base + offset); - - /* Read back to see if val was written */ - tmp = readl_relaxed(base + offset); - tmp &= mask; /* clear other bits */ - - if (tmp != val) - pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", - __func__, val, offset); -} - static int qusb_phy_notify_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -711,18 +689,8 @@ static int qusb_phy_notify_connect(struct usb_phy *phy, qphy->cable_connected = true; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, - UTMI_OTG_VBUS_VALID); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, SW_SESSVLD_SEL); - - dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -733,17 +701,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy, qphy->cable_connected = false; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, 0); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, 0); - - dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -827,16 +786,6 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "qscratch_base"); - if (res) { - qphy->qscratch_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qphy->qscratch_base)) { - dev_dbg(dev, "couldn't ioremap qscratch_base\n"); - qphy->qscratch_base = NULL; - } - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "emu_phy_base"); if (res) { qphy->emu_phy_base = devm_ioremap_resource(dev, res); @@ -1051,11 +1000,8 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.set_suspend = qusb_phy_set_suspend; qphy->phy.shutdown = qusb_phy_shutdown; qphy->phy.type = USB_PHY_TYPE_USB2; - - if (qphy->qscratch_base) { - qphy->phy.notify_connect = qusb_phy_notify_connect; - qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; - } + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; /* * On some platforms multiple QUSB PHYs are available. If QUSB PHY is diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 07e3445c51f7..a99ae97cdb80 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -199,6 +199,97 @@ exit: return rc; } /* mdss_dp_get_dt_clk_data */ +static int mdss_dp_clk_init(struct mdss_dp_drv_pdata *dp_drv, + struct device *dev, bool initialize) +{ + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + int rc = 0; + + if (!dp_drv || !dev) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &dp_drv->power_data[DP_CORE_PM]; + ctrl_power_data = &dp_drv->power_data[DP_CTRL_PM]; + + if (!core_power_data || !ctrl_power_data) { + pr_err("invalid power_data\n"); + rc = -EINVAL; + goto exit; + } + + if (initialize) { + rc = msm_dss_get_clk(dev, core_power_data->clk_config, + core_power_data->num_clk); + if (rc) { + DEV_ERR("Failed to get %s clk. Err=%d\n", + __mdss_dp_pm_name(DP_CORE_PM), rc); + goto exit; + } + + rc = msm_dss_get_clk(dev, ctrl_power_data->clk_config, + ctrl_power_data->num_clk); + if (rc) { + DEV_ERR("Failed to get %s clk. Err=%d\n", + __mdss_dp_pm_name(DP_CTRL_PM), rc); + goto ctrl_get_error; + } + + } else { + msm_dss_put_clk(ctrl_power_data->clk_config, + ctrl_power_data->num_clk); + msm_dss_put_clk(core_power_data->clk_config, + core_power_data->num_clk); + } + + return rc; + +ctrl_get_error: + msm_dss_put_clk(core_power_data->clk_config, + core_power_data->num_clk); + +exit: + return rc; +} + +static int mdss_dp_clk_set_rate_enable( + struct dss_module_power *power_data, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = msm_dss_clk_set_rate( + power_data->clk_config, + power_data->num_clk); + if (ret) { + pr_err("failed to set clks rate.\n"); + goto exit; + } + + ret = msm_dss_enable_clk( + power_data->clk_config, + power_data->num_clk, 1); + if (ret) { + pr_err("failed to enable clks\n"); + goto exit; + } + } else { + ret = msm_dss_enable_clk( + power_data->clk_config, + power_data->num_clk, 0); + if (ret) { + pr_err("failed to disable clks\n"); + goto exit; + } + } +exit: + return ret; +} + /* * This clock control function supports enabling/disabling * of core and ctrl power module clocks @@ -232,35 +323,27 @@ static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv, && (!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); + ret = mdss_dp_clk_set_rate_enable( + &dp_drv->power_data[DP_CORE_PM], + enable); if (ret) { - pr_err("failed to enable clks for %s\n", - __mdss_dp_pm_name(pm_type)); + pr_err("failed to enable clks: %s. err=%d\n", + __mdss_dp_pm_name(DP_CORE_PM), ret); 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; - } + ret = mdss_dp_clk_set_rate_enable( + &dp_drv->power_data[pm_type], + enable); + if (ret) { + pr_err("failed to '%s' clks for: %s. err=%d\n", + enable ? "enable" : "disable", + __mdss_dp_pm_name(pm_type), ret); + goto error; } if (pm_type == DP_CORE_PM) @@ -275,7 +358,7 @@ error: static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv, bool enable) { - int i, ret = 0; + int ret = 0, i = 0, j = 0; if (dp_drv->core_power == enable) { pr_debug("regulators already %s\n", @@ -283,27 +366,23 @@ static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv, 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", + 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, enable); + if (ret) { + pr_err("failed to '%s' vregs for %s\n", + enable ? "enable" : "disable", __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; + if (enable) { + /* Disabling the enabled vregs */ + for (j = i-1; j >= DP_CORE_PM; j--) { + msm_dss_enable_vreg( + dp_drv->power_data[j].vreg_config, + dp_drv->power_data[j].num_vreg, 0); + } } + goto error; } } @@ -472,6 +551,218 @@ static int mdss_dp_regulator_init(struct platform_device *pdev, return rc; } +static int mdss_dp_pinctrl_set_state( + struct mdss_dp_drv_pdata *dp, + bool active) +{ + struct pinctrl_state *pin_state; + int rc = -EFAULT; + + if (IS_ERR_OR_NULL(dp->pin_res.pinctrl)) + return PTR_ERR(dp->pin_res.pinctrl); + + pin_state = active ? dp->pin_res.state_active + : dp->pin_res.state_suspend; + if (!IS_ERR_OR_NULL(pin_state)) { + rc = pinctrl_select_state(dp->pin_res.pinctrl, + pin_state); + if (rc) + pr_err("can not set %s pins\n", + active ? "mdss_dp_active" + : "mdss_dp_sleep"); + } else { + pr_err("invalid '%s' pinstate\n", + active ? "mdss_dp_active" + : "mdss_dp_sleep"); + } + return rc; +} + +static int mdss_dp_pinctrl_init(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + dp->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(dp->pin_res.pinctrl)) { + pr_err("failed to get pinctrl\n"); + return PTR_ERR(dp->pin_res.pinctrl); + } + + dp->pin_res.state_active + = pinctrl_lookup_state(dp->pin_res.pinctrl, + "mdss_dp_active"); + if (IS_ERR_OR_NULL(dp->pin_res.state_active)) { + pr_err("can not get dp active pinstate\n"); + return PTR_ERR(dp->pin_res.state_active); + } + + dp->pin_res.state_suspend + = pinctrl_lookup_state(dp->pin_res.pinctrl, + "mdss_dp_sleep"); + if (IS_ERR_OR_NULL(dp->pin_res.state_suspend)) { + pr_err("can not get dp sleep pinstate\n"); + return PTR_ERR(dp->pin_res.state_suspend); + } + + return 0; +} + +static int mdss_dp_request_gpios(struct mdss_dp_drv_pdata *dp) +{ + int rc = 0; + struct device *dev = NULL; + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + dev = &dp->pdev->dev; + if (gpio_is_valid(dp->aux_en_gpio)) { + rc = devm_gpio_request(dev, dp->aux_en_gpio, + "aux_enable"); + if (rc) { + pr_err("request aux_en gpio failed, rc=%d\n", + rc); + goto aux_en_gpio_err; + } + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + rc = devm_gpio_request(dev, dp->aux_sel_gpio, "aux_sel"); + if (rc) { + pr_err("request aux_sel gpio failed, rc=%d\n", + rc); + goto aux_sel_gpio_err; + } + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + rc = devm_gpio_request(dev, dp->usbplug_cc_gpio, + "usbplug_cc"); + if (rc) { + pr_err("request usbplug_cc gpio failed, rc=%d\n", + rc); + goto usbplug_cc_gpio_err; + } + } + if (gpio_is_valid(dp->hpd_gpio)) { + rc = devm_gpio_request(dev, dp->hpd_gpio, "hpd"); + if (rc) { + pr_err("request hpd gpio failed, rc=%d\n", + rc); + goto hpd_gpio_err; + } + } + return rc; + +hpd_gpio_err: + if (gpio_is_valid(dp->usbplug_cc_gpio)) + gpio_free(dp->usbplug_cc_gpio); +usbplug_cc_gpio_err: + if (gpio_is_valid(dp->aux_sel_gpio)) + gpio_free(dp->aux_sel_gpio); +aux_sel_gpio_err: + if (gpio_is_valid(dp->aux_en_gpio)) + gpio_free(dp->aux_en_gpio); +aux_en_gpio_err: + return rc; +} + +static int mdss_dp_config_gpios(struct mdss_dp_drv_pdata *dp, bool enable) +{ + int rc = 0; + + if (enable == true) { + rc = mdss_dp_request_gpios(dp); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + + if (gpio_is_valid(dp->aux_en_gpio)) { + rc = gpio_direction_output( + dp->aux_en_gpio, 0); + if (rc) + pr_err("unable to set dir for aux_en gpio\n"); + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + rc = gpio_direction_output( + dp->aux_sel_gpio, 0); + if (rc) + pr_err("unable to set dir for aux_sel gpio\n"); + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + gpio_set_value( + dp->usbplug_cc_gpio, 0); + } + if (gpio_is_valid(dp->hpd_gpio)) { + gpio_set_value( + dp->hpd_gpio, 1); + } + } else { + if (gpio_is_valid(dp->aux_en_gpio)) { + gpio_set_value((dp->aux_en_gpio), 0); + gpio_free(dp->aux_en_gpio); + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + gpio_set_value((dp->aux_sel_gpio), 0); + gpio_free(dp->aux_sel_gpio); + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + gpio_set_value((dp->usbplug_cc_gpio), 0); + gpio_free(dp->usbplug_cc_gpio); + } + if (gpio_is_valid(dp->hpd_gpio)) { + gpio_set_value((dp->hpd_gpio), 0); + gpio_free(dp->hpd_gpio); + } + } + return 0; +} + +static int mdss_dp_parse_gpio_params(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + dp->aux_en_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,aux-en-gpio", 0); + + if (!gpio_is_valid(dp->aux_en_gpio)) { + pr_err("%d, Aux_en gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->aux_sel_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,aux-sel-gpio", 0); + + if (!gpio_is_valid(dp->aux_sel_gpio)) { + pr_err("%d, Aux_sel gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->usbplug_cc_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,usbplug-cc-gpio", 0); + + if (!gpio_is_valid(dp->usbplug_cc_gpio)) { + pr_err("%d,usbplug_cc gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->hpd_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,hpd-gpio", 0); + + if (!gpio_is_valid(dp->hpd_gpio)) { + pr_info("%d,hpd gpio not specified\n", + __LINE__); + } + + return 0; +} + void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp) { /* @@ -587,6 +878,8 @@ int mdss_dp_on(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; int ret = 0; + enum plug_orientation orientation = ORIENTATION_NONE; + struct lane_mapping ln_map; if (!pdata) { pr_err("Invalid input data\n"); @@ -596,7 +889,15 @@ int mdss_dp_on(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); - pr_debug("++ cont_splash=%d\n", dp_drv->cont_splash); + /* wait until link training is completed */ + mutex_lock(&dp_drv->host_mutex); + + pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash); + /* Default lane mapping */ + ln_map.lane0 = 2; + ln_map.lane1 = 3; + ln_map.lane2 = 1; + ln_map.lane3 = 0; if (!dp_drv->cont_splash) { /* vote for clocks */ ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); @@ -606,15 +907,52 @@ int mdss_dp_on(struct mdss_panel_data *pdata) } 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); + orientation = usbpd_get_plug_orientation(dp_drv->pd); + pr_debug("plug Orientation = %d\n", orientation); + + if (orientation == ORIENTATION_CC2) { + /* update lane mapping */ + ln_map.lane0 = 1; + ln_map.lane1 = 0; + ln_map.lane2 = 2; + ln_map.lane3 = 3; + + if (gpio_is_valid(dp_drv->usbplug_cc_gpio)) { + gpio_set_value( + dp_drv->usbplug_cc_gpio, 1); + pr_debug("Configured cc gpio for new Orientation\n"); + } + + } + 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); + dp_drv->link_rate = + mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info, + dp_drv->dpcd.max_lane_count); + + pr_debug("link_rate=0x%x, Max rate supported by sink=0x%x\n", + dp_drv->link_rate, dp_drv->dpcd.max_link_rate); + if (!dp_drv->link_rate) { + pr_err("Unable to configure required link rate\n"); + return -EINVAL; + } + + pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); + + dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate = + dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER; + + dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate; + dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate = + dp_drv->pixel_rate; + ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); if (ret) { mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); @@ -624,6 +962,7 @@ int mdss_dp_on(struct mdss_panel_data *pdata) mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + mdss_dp_ctrl_lane_mapping(&dp_drv->ctrl_io, ln_map); reinit_completion(&dp_drv->idle_comp); mdss_dp_fill_link_cfg(dp_drv); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true); @@ -645,7 +984,9 @@ int mdss_dp_on(struct mdss_panel_data *pdata) if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) pr_debug("mainlink ready\n"); + mutex_unlock(&dp_drv->host_mutex); pr_debug("End-\n"); + return ret; } @@ -680,6 +1021,9 @@ int mdss_dp_off(struct mdss_panel_data *pdata) mdss_dp_mainlink_reset(&dp_drv->ctrl_io); mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); + mdss_dp_config_gpios(dp_drv, false); + mdss_dp_pinctrl_set_state(dp_drv, 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); @@ -712,6 +1056,9 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) goto vreg_error; } + mdss_dp_pinctrl_set_state(dp_drv, true); + mdss_dp_config_gpios(dp_drv, true); + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); if (ret) { pr_err("Unabled to start core clocks\n"); @@ -725,6 +1072,10 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_phy_initialize(dp_drv); mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + pr_debug("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)); + return ret; clk_error: @@ -865,7 +1216,7 @@ 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; + u32 todo = 0, dp_config_pkt[2]; if (!dw) { pr_err("invalid work structure\n"); @@ -882,24 +1233,47 @@ static void mdss_dp_event_work(struct work_struct *work) pr_debug("todo=%x\n", todo); switch (todo) { - case (EV_EDID_READ): + case EV_EDID_READ: mdss_dp_edid_read(dp, 0); break; - case (EV_DPCD_CAP_READ): + case EV_DPCD_CAP_READ: mdss_dp_dpcd_cap_read(dp); break; - case (EV_DPCD_STATUS_READ): + case EV_DPCD_STATUS_READ: mdss_dp_dpcd_status_read(dp); break; - case (EV_LINK_TRAIN): + case EV_LINK_TRAIN: mdss_dp_do_link_train(dp); break; - case (EV_VIDEO_READY): + case EV_VIDEO_READY: mdss_dp_video_ready(dp); break; - case (EV_IDLE_PATTERNS_SENT): + case EV_IDLE_PATTERNS_SENT: mdss_dp_idle_patterns_sent(dp); break; + case EV_USBPD_DISCOVER_MODES: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_DISCOVER_MODES, + SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0); + break; + case EV_USBPD_ENTER_MODE: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_ENTER_MODE, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_EXIT_MODE: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_EXIT_MODE, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_DP_STATUS: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_STATUS, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_DP_CONFIGURE: + dp_config_pkt[0] = SVDM_HDR(USB_C_DP_SID, VDM_VERSION, 0x1, + SVDM_CMD_TYPE_INITIATOR, DP_VDM_CONFIGURE); + dp_config_pkt[1] = mdss_dp_usbpd_gen_config_pkt(dp); + usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_CONFIGURE, + SVDM_CMD_TYPE_INITIATOR, 0x1, dp_config_pkt, 0x2); + break; default: pr_err("Unknown event:%d\n", todo); } @@ -987,6 +1361,165 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) return 0; } +static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + mutex_lock(&dp_drv->pd_msg_mutex); + dp_drv->cable_connected = true; + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + mutex_unlock(&dp_drv->pd_msg_mutex); + pr_debug("discover_mode event sent\n"); +} + +static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + pr_debug("cable disconnected\n"); + mutex_lock(&dp_drv->pd_msg_mutex); + dp_drv->cable_connected = false; + mutex_unlock(&dp_drv->pd_msg_mutex); +} + +static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, + const u32 *vdos, int num_vdos) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n", + cmd, *vdos, num_vdos); + + switch (cmd) { + case USBPD_SVDM_DISCOVER_MODES: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.dp_cap.response = *vdos; + mdss_dp_usbpd_ext_capabilities + (&dp_drv->alt_mode.dp_cap); + dp_drv->alt_mode.current_state = DISCOVER_MODES_DONE; + dp_send_events(dp_drv, EV_USBPD_ENTER_MODE); + } else { + pr_err("unknown response: %d for Discover_modes\n", + cmd_type); + } + break; + case USBPD_SVDM_ENTER_MODE: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.current_state = ENTER_MODE_DONE; + dp_send_events(dp_drv, EV_USBPD_DP_STATUS); + } else { + pr_err("unknown response: %d for Enter_mode\n", + cmd_type); + } + break; + case USBPD_SVDM_ATTENTION: + if (cmd_type == SVDM_CMD_TYPE_INITIATOR) { + pr_debug("Attention. cmd_type=%d\n", + cmd_type); + if (!dp_drv->alt_mode.current_state + == ENTER_MODE_DONE) { + pr_debug("sending discover_mode\n"); + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + break; + } + if (num_vdos == 1) { + dp_drv->alt_mode.dp_status.response = *vdos; + mdss_dp_usbpd_ext_dp_status + (&dp_drv->alt_mode.dp_status); + if (dp_drv->alt_mode.dp_status.hpd_high) { + pr_debug("HPD high\n"); + dp_drv->alt_mode.current_state = + DP_STATUS_DONE; + dp_send_events + (dp_drv, EV_USBPD_DP_CONFIGURE); + } + } + } else { + pr_debug("unknown response: %d for Attention\n", + cmd_type); + } + break; + case DP_VDM_STATUS: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.dp_status.response = *vdos; + mdss_dp_usbpd_ext_dp_status + (&dp_drv->alt_mode.dp_status); + if (dp_drv->alt_mode.dp_status.hpd_high) { + pr_debug("HDP high\n"); + dp_drv->alt_mode.current_state = + DP_STATUS_DONE; + dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); + } + } else { + pr_err("unknown response: %d for DP_Status\n", + cmd_type); + } + break; + case DP_VDM_CONFIGURE: + if ((dp_drv->cable_connected == true) + || (cmd_type == SVDM_CMD_TYPE_RESP_ACK)) { + dp_drv->alt_mode.current_state = DP_CONFIGURE_DONE; + pr_debug("config USBPD to DP done\n"); + mdss_dp_host_init(&dp_drv->panel_data); + } else { + pr_err("unknown response: %d for DP_Configure\n", + cmd_type); + } + break; + default: + pr_err("unknown cmd: %d\n", cmd); + break; + } +} + +static int mdss_dp_usbpd_setup(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + const char *pd_phandle = "qcom,dp-usbpd-detection"; + + dp_drv->pd = devm_usbpd_get_by_phandle(&dp_drv->pdev->dev, + pd_phandle); + + if (IS_ERR(dp_drv->pd)) { + pr_err("get_usbpd phandle failed (%ld)\n", + PTR_ERR(dp_drv->pd)); + return PTR_ERR(dp_drv->pd); + } + + dp_drv->svid_handler.svid = USB_C_DP_SID; + dp_drv->svid_handler.vdm_received = NULL; + dp_drv->svid_handler.connect = &usbpd_connect_callback; + dp_drv->svid_handler.svdm_received = &usbpd_response_callback; + dp_drv->svid_handler.disconnect = &usbpd_disconnect_callback; + + ret = usbpd_register_svid(dp_drv->pd, &dp_drv->svid_handler); + if (ret) { + pr_err("usbpd registration failed\n"); + return -ENODEV; + } + + return ret; +} + static int mdss_dp_probe(struct platform_device *pdev) { int ret, i; @@ -1030,8 +1563,17 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->mask1 = EDP_INTR_MASK1; dp_drv->mask2 = EDP_INTR_MASK2; mutex_init(&dp_drv->emutex); + mutex_init(&dp_drv->host_mutex); + mutex_init(&dp_drv->pd_msg_mutex); spin_lock_init(&dp_drv->lock); + if (mdss_dp_usbpd_setup(dp_drv)) { + pr_err("Error usbpd setup!\n"); + devm_kfree(&pdev->dev, dp_drv); + dp_drv = NULL; + return -EPROBE_DEFER; + } + ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv); if (ret) goto probe_err; @@ -1062,6 +1604,14 @@ static int mdss_dp_probe(struct platform_device *pdev) if (ret) goto probe_err; + ret = mdss_dp_clk_init(dp_drv, + &pdev->dev, true); + if (ret) { + DEV_ERR("clk_init failed.ret=%d\n", + ret); + goto probe_err; + } + ret = mdss_dp_irq_setup(dp_drv); if (ret) goto probe_err; @@ -1070,33 +1620,33 @@ static int mdss_dp_probe(struct platform_device *pdev) if (ret) goto probe_err; - ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + 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); + + ret = mdss_dp_pinctrl_init(pdev, dp_drv); if (ret) { - pr_err("Unabled to enable core clocks\n"); + pr_err("pinctrl init failed, ret=%d\n", + ret); 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); + ret = mdss_dp_parse_gpio_params(pdev, dp_drv); if (ret) { - pr_err("Unabled to disable core clocks\n"); + pr_err("failed to parse gpio params, ret=%d\n", + ret); 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"); + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + return 0; probe_err: diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index b63318dcca06..008e7d687dbd 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -20,6 +20,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/gpio.h> #include <linux/of_gpio.h> +#include <linux/usb/usbpd.h> #include "mdss_hdmi_util.h" #include "video/msm_hdmi_modes.h" @@ -103,10 +104,6 @@ #define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2) - -#define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510 -#define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514 - struct edp_cmd { char read; /* 1 == read, 0 == write */ char i2c; /* 1 == i2c cmd, 0 == native cmd */ @@ -126,6 +123,10 @@ struct edp_buf { char i2c; /* 1 == i2c cmd, 0 == native cmd */ }; +/* USBPD-TypeC specific Macros */ +#define VDM_VERSION 0x0 +#define USB_C_DP_SID 0xFF01 + enum dp_pm_type { DP_CORE_PM, DP_CTRL_PM, @@ -133,6 +134,64 @@ enum dp_pm_type { DP_MAX_PM }; +#define PIN_ASSIGN_A BIT(0) +#define PIN_ASSIGN_B BIT(1) +#define PIN_ASSIGN_C BIT(2) +#define PIN_ASSIGN_D BIT(3) +#define PIN_ASSIGN_E BIT(4) +#define PIN_ASSIGN_F BIT(5) + +#define SVDM_HDR(svid, ver, mode, cmd_type, cmd) \ + (((svid) << 16) | (1 << 15) | ((ver) << 13) \ + | ((mode) << 8) | ((cmd_type) << 6) | (cmd)) + +/* DP specific VDM commands */ +#define DP_VDM_STATUS 0x10 +#define DP_VDM_CONFIGURE 0x11 + +enum dp_port_cap { + PORT_NONE = 0, + PORT_UFP_D, + PORT_DFP_D, + PORT_D_UFP_D, +}; + +struct usbpd_dp_capabilities { + u32 response; + enum dp_port_cap s_port; + bool receptacle_state; + u8 ulink_pin_config; + u8 dlink_pin_config; +}; + +struct usbpd_dp_status { + u32 response; + enum dp_port_cap c_port; + bool low_pow_st; + bool adaptor_dp_en; + bool multi_func; + bool switch_to_usb_config; + bool exit_dp_mode; + bool hpd_high; + bool hpd_irq; +}; + +enum dp_alt_mode_state { + ALT_MODE_INIT_STATE = 0, + DISCOVER_MODES_DONE, + ENTER_MODE_DONE, + DP_STATUS_DONE, + DP_CONFIGURE_DONE, + UNKNOWN_STATE, +}; + +struct dp_alt_mode { + struct usbpd_dp_capabilities dp_cap; + struct usbpd_dp_status dp_status; + u32 usbpd_dp_config; + enum dp_alt_mode_state current_state; +}; + #define DPCD_ENHANCED_FRAME BIT(0) #define DPCD_TPS3 BIT(1) #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) @@ -145,18 +204,26 @@ enum dp_pm_type { #define EV_DPCD_CAP_READ BIT(2) #define EV_DPCD_STATUS_READ BIT(3) #define EV_LINK_TRAIN BIT(4) -#define EV_IDLE_PATTERNS_SENT BIT(30) -#define EV_VIDEO_READY BIT(31) +#define EV_IDLE_PATTERNS_SENT BIT(5) +#define EV_VIDEO_READY BIT(6) -/* edp state ctrl */ +#define EV_USBPD_DISCOVER_MODES BIT(7) +#define EV_USBPD_ENTER_MODE BIT(8) +#define EV_USBPD_DP_STATUS BIT(9) +#define EV_USBPD_DP_CONFIGURE BIT(10) +#define EV_USBPD_CC_PIN_POLARITY BIT(11) +#define EV_USBPD_EXIT_MODE BIT(12) + +/* dp state ctrl */ #define ST_TRAIN_PATTERN_1 BIT(0) #define ST_TRAIN_PATTERN_2 BIT(1) #define ST_TRAIN_PATTERN_3 BIT(2) -#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(3) -#define ST_PRBS7 BIT(4) -#define ST_CUSTOM_80_BIT_PATTERN BIT(5) -#define ST_SEND_VIDEO BIT(6) -#define ST_PUSH_IDLE BIT(7) +#define ST_TRAIN_PATTERN_4 BIT(3) +#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(4) +#define ST_PRBS7 BIT(5) +#define ST_CUSTOM_80_BIT_PATTERN BIT(6) +#define ST_SEND_VIDEO BIT(7) +#define ST_PUSH_IDLE BIT(8) /* sink power state */ #define SINK_POWER_ON 1 @@ -167,6 +234,8 @@ enum dp_pm_type { #define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */ #define DP_LINK_RATE_MAX DP_LINK_RATE_540 +#define DP_LINK_RATE_MULTIPLIER 27000000 + struct dpcd_cap { char major; char minor; @@ -254,10 +323,16 @@ struct dp_statistic { u32 aux_native_rx; }; - #define DPCD_LINK_VOLTAGE_MAX 4 #define DPCD_LINK_PRE_EMPHASIS_MAX 4 +struct dp_pinctrl_res { + struct pinctrl *pinctrl; + struct pinctrl_state *state_active; + struct pinctrl_state *state_hpd_active; + struct pinctrl_state *state_suspend; +}; + irqreturn_t dp_isr(int irq, void *ptr); struct mdss_dp_drv_pdata { @@ -266,6 +341,10 @@ struct mdss_dp_drv_pdata { int (*off) (struct mdss_panel_data *pdata); struct platform_device *pdev; + struct usbpd *pd; + struct usbpd_svid_handler svid_handler; + struct dp_alt_mode alt_mode; + struct mutex emutex; int clk_cnt; int cont_splash; @@ -302,6 +381,11 @@ struct mdss_dp_drv_pdata { /* regulators */ struct dss_module_power power_data[DP_MAX_PM]; + struct dp_pinctrl_res pin_res; + int aux_sel_gpio; + int aux_en_gpio; + int usbplug_cc_gpio; + int hpd_gpio; int clk_on; /* hpd */ @@ -316,6 +400,9 @@ struct mdss_dp_drv_pdata { struct completion video_comp; struct mutex aux_mutex; struct mutex train_mutex; + struct mutex host_mutex; + struct mutex pd_msg_mutex; + bool cable_connected; u32 aux_cmd_busy; u32 aux_cmd_i2c; int aux_trans_num; @@ -379,5 +466,6 @@ 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); +char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt); #endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 39f11a8c35d1..7b14a7efb9dc 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -484,6 +484,37 @@ void dp_extract_edid_feature(struct edp_edid *edid, char *buf) edid->dpm, edid->color_format); }; +char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt) +{ + const u32 encoding_factx10 = 8; + const u32 ln_to_link_ratio = 10; + u32 min_link_rate; + char calc_link_rate = 0; + + pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n", + pinfo->clk_rate, pinfo->bpp, lane_cnt); + min_link_rate = (pinfo->clk_rate * 10) / + (lane_cnt * encoding_factx10); + min_link_rate = (min_link_rate * pinfo->bpp) + / (DP_LINK_RATE_MULTIPLIER); + min_link_rate /= ln_to_link_ratio; + + pr_debug("min_link_rate = %d\n", min_link_rate); + + if (min_link_rate <= DP_LINK_RATE_162) + calc_link_rate = DP_LINK_RATE_162; + else if (min_link_rate <= DP_LINK_RATE_270) + calc_link_rate = DP_LINK_RATE_270; + else if (min_link_rate <= DP_LINK_RATE_540) + calc_link_rate = DP_LINK_RATE_540; + else { + pr_err("link_rate = %d is unsupported\n", min_link_rate); + calc_link_rate = 0; + } + + return calc_link_rate; +} + void dp_extract_edid_detailed_timing_description(struct edp_edid *edid, char *buf) { @@ -1037,23 +1068,37 @@ char vm_voltage_swing[4][4] = { {0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; -static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *ep) +static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp) { u32 value0 = 0; u32 value1 = 0; - pr_debug("v=%d p=%d\n", ep->v_level, ep->p_level); + pr_debug("v=%d p=%d\n", dp->v_level, dp->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)]; + value0 = vm_voltage_swing[(int)(dp->v_level)][(int)(dp->p_level)]; + value1 = vm_pre_emphasis[(int)(dp->v_level)][(int)(dp->p_level)]; + /* Enable MUX to use Cursor values from these registers */ + value0 |= BIT(5); + value1 |= BIT(5); /* Configure host and panel only if both values are allowed */ if (value0 != 0xFF && value1 != 0xFF) { - dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0); - dp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1); + dp_write(dp->phy_io.base + + QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL, + value0); + dp_write(dp->phy_io.base + + QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL, + value0); + dp_write(dp->phy_io.base + + QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, + value1); + dp_write(dp->phy_io.base + + QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, + value1); + pr_debug("value0=0x%x value1=0x%x", value0, value1); - dp_lane_set_write(ep, ep->v_level, ep->p_level); + dp_lane_set_write(dp, dp->v_level, dp->p_level); } } @@ -1212,34 +1257,36 @@ static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep) usleep_range(usleep_time, usleep_time); } -static int dp_aux_link_train(struct mdss_dp_drv_pdata *ep) +static int dp_aux_link_train(struct mdss_dp_drv_pdata *dp) { int ret = 0; int usleep_time; - ret = dp_aux_chan_ready(ep); + ret = dp_aux_chan_ready(dp); if (ret) { pr_err("LINK Train failed: aux chan NOT ready\n"); - complete(&ep->train_comp); + complete(&dp->train_comp); return ret; } - dp_write(ep->base + DP_MAINLINK_CTRL, 0x1); + dp_write(dp->base + DP_MAINLINK_CTRL, 0x1); - mdss_dp_sink_power_state(ep, SINK_POWER_ON); + mdss_dp_sink_power_state(dp, SINK_POWER_ON); train_start: - ep->v_level = 0; /* start from default level */ - ep->p_level = 0; - dp_cap_lane_rate_set(ep); - - dp_clear_training_pattern(ep); - usleep_time = ep->dpcd.training_read_interval; + dp->v_level = 0; /* start from default level */ + dp->p_level = 0; + dp_cap_lane_rate_set(dp); + mdss_dp_config_ctrl(dp); + + mdss_dp_state_ctrl(&dp->ctrl_io, 0); + dp_clear_training_pattern(dp); + usleep_time = dp->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - ret = dp_start_link_train_1(ep); + ret = dp_start_link_train_1(dp); if (ret < 0) { - if (dp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(dp) == 0) { goto train_start; } else { pr_err("Training 1 failed\n"); @@ -1250,10 +1297,11 @@ train_start: pr_debug("Training 1 completed successfully\n"); - dp_clear_training_pattern(ep); - ret = dp_start_link_train_2(ep); + mdss_dp_state_ctrl(&dp->ctrl_io, 0); + dp_clear_training_pattern(dp); + ret = dp_start_link_train_2(dp); if (ret < 0) { - if (dp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(dp) == 0) { goto train_start; } else { pr_err("Training 2 failed\n"); @@ -1264,10 +1312,16 @@ train_start: pr_debug("Training 2 completed successfully\n"); + clear: - dp_clear_training_pattern(ep); + dp_clear_training_pattern(dp); + if (ret != -1) { + mdss_dp_setup_tr_unit(&dp->ctrl_io); + mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO); + pr_debug("State_ctrl set to SEND_VIDEO\n"); + } - complete(&ep->train_comp); + complete(&dp->train_comp); return ret; } diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index c1d29987a5fa..f7b27d1e56a1 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -214,14 +214,24 @@ void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io) writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID); } +void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io) +{ + /* Current Tr unit configuration supports only 1080p */ + writel_relaxed(0x21, ctrl_io->base + DP_MISC1_MISC0); + writel_relaxed(0x0f0016, ctrl_io->base + DP_VALID_BOUNDARY); + writel_relaxed(0x1f, ctrl_io->base + DP_TU); + writel_relaxed(0x0, ctrl_io->base + DP_VALID_BOUNDARY_2); +} + 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))); + | (l_map.lane1 << (bits_per_lane * 1)) + | (l_map.lane2 << (bits_per_lane * 2)) + | (l_map.lane3 << (bits_per_lane * 3))); + pr_debug("%s: lane mapping reg = 0x%x\n", __func__, lane_map); writel_relaxed(lane_map, ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); } @@ -282,3 +292,81 @@ void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv) dp_drv->mdss_util->disable_irq(&mdss_dp_hw); } + +static void mdss_dp_initialize_s_port(enum dp_port_cap *s_port, int port) +{ + switch (port) { + case 0: + *s_port = PORT_NONE; + break; + case 1: + *s_port = PORT_UFP_D; + break; + case 2: + *s_port = PORT_DFP_D; + break; + case 3: + *s_port = PORT_D_UFP_D; + break; + default: + *s_port = PORT_NONE; + } +} + +void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap) +{ + u32 buf = dp_cap->response; + int port = buf & 0x3; + + dp_cap->receptacle_state = + (buf & BIT(6)) ? true : false; + + dp_cap->dlink_pin_config = + (buf >> 8) & 0xff; + + dp_cap->ulink_pin_config = + (buf >> 16) & 0xff; + + mdss_dp_initialize_s_port(&dp_cap->s_port, port); +} + +void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status) +{ + u32 buf = dp_status->response; + int port = buf & 0x3; + + dp_status->low_pow_st = + (buf & BIT(2)) ? true : false; + + dp_status->adaptor_dp_en = + (buf & BIT(3)) ? true : false; + + dp_status->multi_func = + (buf & BIT(4)) ? true : false; + + dp_status->switch_to_usb_config = + (buf & BIT(5)) ? true : false; + + dp_status->exit_dp_mode = + (buf & BIT(6)) ? true : false; + + dp_status->hpd_high = + (buf & BIT(7)) ? true : false; + + dp_status->hpd_irq = + (buf & BIT(8)) ? true : false; + + mdss_dp_initialize_s_port(&dp_status->c_port, port); +} + +u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp) +{ + u32 config = 0; + + config |= (dp->alt_mode.dp_cap.dlink_pin_config << 8); + config |= (0x1 << 2); /* configure for DPv1.3 */ + config |= 0x2; /* Configuring for UFP_D */ + + pr_debug("DP config = 0x%x\n", config); + return config; +} diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index d9064cafad9a..8ef00dd7248e 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -48,10 +48,13 @@ #define DP_START_HOR_VER_FROM_SYNC (0x00000420) #define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424) #define DP_ACTIVE_HOR_VER (0x00000428) - +#define DP_MISC1_MISC0 (0x0000042C) +#define DP_VALID_BOUNDARY (0x00000430) +#define DP_VALID_BOUNDARY_2 (0x00000434) #define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438) #define DP_MAINLINK_READY (0x00000440) +#define DP_TU (0x0000044C) /*DP PHY Register offsets */ #define DP_PHY_REVISION_ID0 (0x00000000) @@ -76,6 +79,12 @@ #define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) #define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) +#define QSERDES_TX0_OFFSET 0x0400 +#define QSERDES_TX1_OFFSET 0x0800 + +#define TXn_TX_EMP_POST1_LVL 0x000C +#define TXn_TX_DRV_LVL 0x001C + #define TCSR_USB3_DP_PHYMODE 0x48 struct lane_mapping { @@ -85,6 +94,7 @@ struct lane_mapping { char lane3; }; +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); 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); @@ -92,6 +102,7 @@ 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_setup_tr_unit(struct dss_io_data *ctrl_io); 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); @@ -107,5 +118,10 @@ 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); +void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap); +void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status); +u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp); +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map); #endif /* __DP_UTIL_H__ */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 63568caf0de8..e0b0d2b12b88 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3042,6 +3042,24 @@ struct wiphy_vendor_command { }; /** + * struct wiphy_iftype_ext_capab - extended capabilities per interface type + * @iftype: interface type + * @extended_capabilities: extended capabilities supported by the driver, + * additional capabilities might be supported by userspace; these are the + * 802.11 extended capabilities ("Extended Capabilities element") and are + * in the same format as in the information element. See IEEE Std + * 802.11-2012 8.4.2.29 for the defined fields. + * @extended_capabilities_mask: mask of the valid values + * @extended_capabilities_len: length of the extended capabilities + */ +struct wiphy_iftype_ext_capab { + enum nl80211_iftype iftype; + const u8 *extended_capabilities; + const u8 *extended_capabilities_mask; + u8 extended_capabilities_len; +}; + +/** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, * note that if your driver uses wiphy_apply_custom_regulatory() @@ -3158,9 +3176,14 @@ struct wiphy_vendor_command { * additional capabilities might be supported by userspace; these are * the 802.11 extended capabilities ("Extended Capabilities element") * and are in the same format as in the information element. See - * 802.11-2012 8.4.2.29 for the defined fields. + * 802.11-2012 8.4.2.29 for the defined fields. These are the default + * extended capabilities to be used if the capabilities are not specified + * for a specific interface type in iftype_ext_capab. * @extended_capabilities_mask: mask of the valid values * @extended_capabilities_len: length of the extended capabilities + * @iftype_ext_capab: array of extended capabilities per interface type + * @num_iftype_ext_capab: number of interface types for which extended + * capabilities are specified separately. * @coalesce: packet coalescing support information * * @vendor_commands: array of vendor commands supported by the hardware @@ -3257,6 +3280,9 @@ struct wiphy { const u8 *extended_capabilities, *extended_capabilities_mask; u8 extended_capabilities_len; + const struct wiphy_iftype_ext_capab *iftype_ext_capab; + unsigned int num_iftype_ext_capab; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 25627f584405..b5323800eeb5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1764,8 +1764,9 @@ enum nl80211_commands { * over all channels. * * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a - * scheduled scan (or a WoWLAN net-detect scan) is started, u32 - * in seconds. + * scheduled scan is started. Or the delay before a WoWLAN + * net-detect scan is started, counting from the moment the + * system is suspended. This value is a u32, in seconds. * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device * is operating in an indoor environment. @@ -1782,11 +1783,26 @@ enum nl80211_commands { * thus it must not specify the number of iterations, only the interval * between scans. The scan plans are executed sequentially. * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan. - * * @NL80211_ATTR_PBSS: flag attribute. If set it means operate * in a PBSS. Specified in %NL80211_CMD_CONNECT to request * connecting to a PCP, and in %NL80211_CMD_START_AP to start * a PCP instead of AP. Relevant for DMG networks only. + * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the + * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains + * attributes according &enum nl80211_bss_select_attr to indicate what + * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT + * it contains the behaviour-specific attribute containing the parameters for + * BSS selection to be done by driver and/or firmware. + * + * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported + * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status + * + * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment + * + * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes: + * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA, + * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per + * interface type. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -2164,6 +2180,14 @@ enum nl80211_attrs { NL80211_ATTR_PBSS, + NL80211_ATTR_BSS_SELECT, + + NL80211_ATTR_STA_SUPPORT_P2P_PS, + + NL80211_ATTR_PAD, + + NL80211_ATTR_IFTYPE_EXT_CAPA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/kernel/drivers/input/touchscreen/synaptics_fw_update.c b/kernel/drivers/input/touchscreen/synaptics_fw_update.c index 4867d1f73c4d..8b6d7c7e368d 100644 --- a/kernel/drivers/input/touchscreen/synaptics_fw_update.c +++ b/kernel/drivers/input/touchscreen/synaptics_fw_update.c @@ -30,7 +30,10 @@ #define DEBUG_FW_UPDATE #define SHOW_PROGRESS -#define FW_IMAGE_NAME "PR12345678.img" +#define FW_IMAGE_NAME "PR1063486-s7301_00000000.img" +#define MAX_FIRMWARE_ID_LEN 10 +#define FORCE_UPDATE false +#define INSIDE_FIRMWARE_UPDATE #define CHECKSUM_OFFSET 0x00 #define BOOTLOADER_VERSION_OFFSET 0x07 @@ -73,6 +76,12 @@ enum flash_command { CMD_ENABLE_FLASH_PROG = 0xF, }; +enum flash_area { + NONE, + UI_FIRMWARE, + CONFIG_AREA +}; + #define SLEEP_MODE_NORMAL (0x00) #define SLEEP_MODE_SENSOR_SLEEP (0x01) #define SLEEP_MODE_RESERVED0 (0x02) @@ -81,9 +90,9 @@ enum flash_command { #define ENABLE_WAIT_MS (1 * 1000) #define WRITE_WAIT_MS (3 * 1000) #define ERASE_WAIT_MS (5 * 1000) +#define RESET_WAIT_MS (500) -#define MIN_SLEEP_TIME_US 50 -#define MAX_SLEEP_TIME_US 100 +#define SLEEP_TIME_US 50 static ssize_t fwu_sysfs_show_image(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, @@ -204,6 +213,7 @@ struct f34_flash_properties { struct synaptics_rmi4_fwu_handle { bool initialized; + bool force_update; char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; unsigned int image_size; unsigned int data_pos; @@ -231,6 +241,8 @@ struct synaptics_rmi4_fwu_handle { struct synaptics_rmi4_data *rmi4_data; struct f34_flash_control flash_control; struct f34_flash_properties flash_properties; + struct workqueue_struct *fwu_workqueue; + struct delayed_work fwu_work; }; static struct bin_attribute dev_attr_data = { @@ -313,53 +325,6 @@ static void parse_header(struct image_header *header, return; } -static int fwu_check_version(void) -{ - int retval; - unsigned char firmware_id[4]; - unsigned char config_id[4]; - struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; - - /* device firmware id */ - retval = fwu->fn_ptr->read(fwu->rmi4_data, - fwu->f01_fd.query_base_addr + 18, - firmware_id, - sizeof(firmware_id)); - if (retval < 0) { - dev_err(&i2c_client->dev, - "Failed to read firmware ID (code %d).\n", retval); - return retval; - } - firmware_id[3] = 0; - - dev_info(&i2c_client->dev, "Device firmware ID%d\n", - extract_uint(firmware_id)); - - /* device config id */ - retval = fwu->fn_ptr->read(fwu->rmi4_data, - fwu->f34_fd.ctrl_base_addr, - config_id, - sizeof(config_id)); - if (retval < 0) { - dev_err(&i2c_client->dev, - "Failed to read config ID (code %d).\n", retval); - return retval; - } - - dev_info(&i2c_client->dev, - "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", - config_id[0], config_id[1], config_id[2], config_id[3]); - - /* .img config id */ - dev_info(&i2c_client->dev, - ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", - fwu->config_data[0], - fwu->config_data[1], - fwu->config_data[2], - fwu->config_data[3]); - return 0; -} - static int fwu_read_f01_device_status(struct f01_device_status *status) { int retval; @@ -407,7 +372,7 @@ static int fwu_read_f34_queries(void) return retval; } - dev_info(&i2c_client->dev, "%s perm:%d, bl%d, display:%d\n", + dev_info(&i2c_client->dev, "%s perm:%d, bl:%d, display:%d\n", __func__, fwu->flash_properties.has_perm_config, fwu->flash_properties.has_bl_config, @@ -506,25 +471,13 @@ static int fwu_read_f34_flash_status(void) static int fwu_reset_device(void) { int retval; - unsigned char reset = 0x01; #ifdef DEBUG_FW_UPDATE - dev_info(&fwu->rmi4_data->i2c_client->dev, "Reset device\n"); + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: Reset device\n", + __func__); #endif - retval = fwu->fn_ptr->write(fwu->rmi4_data, - fwu->f01_fd.cmd_base_addr, - &reset, - sizeof(reset)); - if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Failed to reset device (addr : 0x%02x)\n", - __func__, fwu->f01_fd.cmd_base_addr); - return retval; - } - - fwu_wait_for_idle(WRITE_WAIT_MS); - retval = fwu->rmi4_data->reset_device(fwu->rmi4_data); if (retval < 0) { dev_err(&fwu->rmi4_data->i2c_client->dev, @@ -539,37 +492,34 @@ static int fwu_write_f34_command(unsigned char cmd) { int retval; + fwu->flash_control.data[0] = cmd; retval = fwu->fn_ptr->write(fwu->rmi4_data, fwu->addr_f34_flash_control, - &cmd, - sizeof(cmd)); + fwu->flash_control.data, + sizeof(fwu->flash_control.data)); if (retval < 0) { dev_err(&fwu->rmi4_data->i2c_client->dev, "%s: Failed to write command 0x%02x\n", - __func__, cmd); + __func__, fwu->flash_control.data[0]); return retval; } return 0; } -static unsigned char fwu_check_flash_status(void) -{ - fwu_read_f34_flash_status(); - return fwu->flash_control.status; -} - static int fwu_wait_for_idle(int timeout_ms) { int count = 0; - int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; - + int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1; do { - if (fwu_read_interrupt_status() > 0) + if (fwu->flash_control.command == 0x00) return 0; - usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); - count++; - } while (count < timeout_count); + usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 100); + } while (count++ < timeout_count); + + fwu_read_f34_flash_status(); + if (fwu->flash_control.command == 0x00) + return 0; dev_err(&fwu->rmi4_data->i2c_client->dev, "%s: Timed out waiting for idle status\n", @@ -578,6 +528,133 @@ static int fwu_wait_for_idle(int timeout_ms) return -ETIMEDOUT; } +static enum flash_area fwu_go_nogo(void) +{ + int retval = 0; + int index = 0; + int deviceFirmwareID; + int imageConfigID; + int deviceConfigID; + unsigned long imageFirmwareID; + unsigned char firmware_id[4]; + unsigned char config_id[4]; + char *strptr; + char *imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL); + enum flash_area flash_area = NONE; + struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; + struct f01_device_status f01_device_status; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + + imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL); + + /* Force update firmware when device is in bootloader mode */ + if (f01_device_status.flash_prog) { + dev_info(&i2c_client->dev, + "%s: In flash prog mode\n", + __func__); + flash_area = UI_FIRMWARE; + goto exit; + } + + + /* device firmware id */ + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f01_fd.query_base_addr + 18, + firmware_id, + sizeof(firmware_id)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "Failed to read firmware ID (code %d).\n", retval); + goto exit; + } + firmware_id[3] = 0; + deviceFirmwareID = extract_uint(firmware_id); + + /* .img firmware id */ + strptr = strstr(FW_IMAGE_NAME, "PR"); + if (!strptr) { + dev_err(&i2c_client->dev, + "No valid PR number (PRxxxxxxx)" \ + "found in image file name...\n"); + goto exit; + } + + strptr += 2; + while (strptr[index] >= '0' && strptr[index] <= '9') { + imagePR[index] = strptr[index]; + index++; + } + imagePR[index] = 0; + + retval = sstrtoul(imagePR, 10, &imageFirmwareID); + if (retval == -EINVAL) { + dev_err(&i2c_client->dev, + "invalid image firmware id...\n"); + goto exit; + } + + dev_info(&i2c_client->dev, + "Device firmware id %d, .img firmware id %d\n", + deviceFirmwareID, + (unsigned int)imageFirmwareID); + if (imageFirmwareID > deviceFirmwareID) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* device config id */ + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.ctrl_base_addr, + config_id, + sizeof(config_id)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "Failed to read config ID (code %d).\n", retval); + flash_area = NONE; + goto exit; + } + deviceConfigID = extract_uint(config_id); + + dev_info(&i2c_client->dev, + "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + config_id[0], config_id[1], config_id[2], config_id[3]); + + /* .img config id */ + dev_info(&i2c_client->dev, + ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + fwu->config_data[0], + fwu->config_data[1], + fwu->config_data[2], + fwu->config_data[3]); + imageConfigID = extract_uint(fwu->config_data); + + if (imageConfigID > deviceConfigID) { + flash_area = CONFIG_AREA; + goto exit; + } + +exit: + kfree(imagePR); + if (flash_area == NONE) + dev_info(&i2c_client->dev, + "Nothing needs to be updated\n"); + else + dev_info(&i2c_client->dev, + "Update %s block\n", + flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG"); + return flash_area; +} + static int fwu_scan_pdt(void) { int retval; @@ -649,16 +726,25 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, int retval; unsigned char block_offset[] = {0, 0}; unsigned short block_num; + struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; #ifdef SHOW_PROGRESS unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ? 10 : 100; #endif + +#ifdef DEBUG_FW_UPDATE + dev_info(&i2c_client->dev, + "%s: Start to update %s blocks\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware"); +#endif retval = fwu->fn_ptr->write(fwu->rmi4_data, fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET, block_offset, sizeof(block_offset)); if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, + dev_err(&i2c_client->dev, "%s: Failed to write to block number registers\n", __func__); return retval; @@ -667,20 +753,19 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, for (block_num = 0; block_num < block_cnt; block_num++) { #ifdef SHOW_PROGRESS if (block_num % progress == 0) - dev_info(&fwu->rmi4_data->i2c_client->dev, - "%s: update %s %3d / %3d\n", - __func__, - command == CMD_WRITE_CONFIG_BLOCK ? - "config" : "firmware", - block_num, - block_cnt); + dev_info(&i2c_client->dev, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware", + block_num, block_cnt); #endif retval = fwu->fn_ptr->write(fwu->rmi4_data, fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET, block_ptr, fwu->block_size); if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, + dev_err(&i2c_client->dev, "%s: Failed to write block data (block %d)\n", __func__, block_num); return retval; @@ -688,7 +773,7 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, retval = fwu_write_f34_command(command); if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, + dev_err(&i2c_client->dev, "%s: Failed to write command for block %d\n", __func__, block_num); return retval; @@ -696,30 +781,28 @@ static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, retval = fwu_wait_for_idle(WRITE_WAIT_MS); if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Failed to wait for idle status \ - (block %d)\n", - __func__, block_num); + dev_err(&i2c_client->dev, + "%s: Failed to wait for idle status (block %d)\n", + __func__, block_num); return retval; } - retval = fwu_check_flash_status(); - if (retval != 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Flash block %d status %d\n", + if (fwu->flash_control.status != 0x00) { + dev_err(&i2c_client->dev, + "%s: Flash block %d failed, status 0x%02X\n", __func__, block_num, retval); return -1; } + block_ptr += fwu->block_size; } #ifdef SHOW_PROGRESS - dev_info(&fwu->rmi4_data->i2c_client->dev, - "%s: update %s %3d / %3d\n", - __func__, - command == CMD_WRITE_CONFIG_BLOCK ? - "config" : "firmware", - block_cnt, - block_cnt); + dev_info(&i2c_client->dev, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware", + block_cnt, block_cnt); #endif return 0; } @@ -741,7 +824,10 @@ static int fwu_write_bootloader_id(void) int retval; #ifdef DEBUG_FW_UPDATE - dev_info(&fwu->rmi4_data->i2c_client->dev, "Write bootloader ID\n"); + dev_info(&fwu->rmi4_data->i2c_client->dev, + "Write bootloader ID 0x%02X 0x%02X\n", + fwu->bootloader_id[0], + fwu->bootloader_id[1]); #endif retval = fwu->fn_ptr->write(fwu->rmi4_data, fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET, @@ -789,17 +875,6 @@ static int fwu_enter_flash_prog(void) if (retval < 0) return retval; - retval = fwu_read_f01_device_status(&f01_device_status); - if (retval < 0) - return retval; - - if (!f01_device_status.flash_prog) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Program enabled bit not set\n", - __func__); - return -EINVAL; - } - retval = fwu_scan_pdt(); if (retval < 0) return retval; @@ -879,9 +954,12 @@ static int fwu_do_reflash(void) if (retval < 0) return retval; - dev_dbg(&fwu->rmi4_data->i2c_client->dev, - "%s: Idle status detected\n", - __func__); + if (fwu->flash_control.status != 0x00) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Erase all command failed, status 0x%02X\n", + __func__, retval); + return -1; + } if (fwu->firmware_data) { retval = fwu_write_firmware(); @@ -900,90 +978,6 @@ static int fwu_do_reflash(void) return retval; } -static int fwu_start_reflash(void) -{ - int retval; - struct image_header header; - const unsigned char *fw_image; - const struct firmware *fw_entry = NULL; - struct f01_device_status f01_device_status; - - pr_notice("%s: Start of reflash process\n", __func__); - - if (fwu->ext_data_source) - fw_image = fwu->ext_data_source; - else { - dev_dbg(&fwu->rmi4_data->i2c_client->dev, - "%s: Requesting firmware image %s\n", - __func__, FW_IMAGE_NAME); - - retval = request_firmware(&fw_entry, FW_IMAGE_NAME, - &fwu->rmi4_data->i2c_client->dev); - if (retval != 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Firmware image %s not available\n", - __func__, FW_IMAGE_NAME); - retval = -EINVAL; - goto exit; - } - - dev_dbg(&fwu->rmi4_data->i2c_client->dev, - "%s: Firmware image size = %d\n", - __func__, fw_entry->size); - - fw_image = fw_entry->data; - } - - parse_header(&header, fw_image); - - if (header.image_size) - fwu->firmware_data = fw_image + FW_IMAGE_OFFSET; - if (header.config_size) { - fwu->config_data = fw_image + FW_IMAGE_OFFSET + - header.image_size; - } - - fwu->fn_ptr->enable(fwu->rmi4_data, false); - - fwu_check_version(); - - retval = fwu_do_reflash(); - if (retval < 0) { - dev_err(&fwu->rmi4_data->i2c_client->dev, - "%s: Failed to do reflash\n", - __func__); - } - - /* reset device */ - fwu_reset_device(); - - /* check device status */ - retval = fwu_read_f01_device_status(&f01_device_status); - if (retval < 0) - goto exit; - - dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n", - f01_device_status.flash_prog == 1 ? "bootloader" : "UI"); - if (f01_device_status.flash_prog) - dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n", - f01_device_status.status_code); - - if (f01_device_status.flash_prog) { - dev_info(&fwu->rmi4_data->i2c_client->dev, - "%s: Device is in flash prog mode 0x%02X\n", - __func__, f01_device_status.status_code); - retval = 0; - goto exit; - } - fwu->fn_ptr->enable(fwu->rmi4_data, true); - if (fw_entry) - release_firmware(fw_entry); - - pr_notice("%s: End of reflash process\n", __func__); -exit: - return retval; -} - static int fwu_do_write_config(void) { int retval; @@ -1205,6 +1199,110 @@ exit: return retval; } +static int fwu_start_reflash(void) +{ + int retval; + struct image_header header; + const unsigned char *fw_image; + const struct firmware *fw_entry = NULL; + struct f01_device_status f01_device_status; + enum flash_area flash_area; + + pr_notice("%s: Start of reflash process\n", __func__); + + if (fwu->ext_data_source) + fw_image = fwu->ext_data_source; + else { + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Requesting firmware image %s\n", + __func__, FW_IMAGE_NAME); + + retval = request_firmware(&fw_entry, FW_IMAGE_NAME, + &fwu->rmi4_data->i2c_client->dev); + if (retval != 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Firmware image %s not available\n", + __func__, FW_IMAGE_NAME); + retval = -EINVAL; + goto exit; + } + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Firmware image size = %d\n", + __func__, fw_entry->size); + + fw_image = fw_entry->data; + } + + parse_header(&header, fw_image); + + if (header.image_size) + fwu->firmware_data = fw_image + FW_IMAGE_OFFSET; + if (header.config_size) { + fwu->config_data = fw_image + FW_IMAGE_OFFSET + + header.image_size; + } + + if (fwu->ext_data_source) + flash_area = UI_FIRMWARE; + else + flash_area = fwu_go_nogo(); + + switch (flash_area) { + case NONE: + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: No need to do reflash.\n", + __func__); + goto exit; + case UI_FIRMWARE: + retval = fwu_do_reflash(); + break; + case CONFIG_AREA: + retval = fwu_do_write_config(); + break; + default: + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Unknown flash area\n", + __func__); + goto exit; + } + + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to do reflash\n", + __func__); + } + + /* reset device */ + fwu_reset_device(); + + /* check device status */ + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + goto exit; + + dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n", + f01_device_status.flash_prog == 1 ? "bootloader" : "UI"); + if (f01_device_status.flash_prog) + dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n", + f01_device_status.status_code); + + if (f01_device_status.flash_prog) { + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: Device is in flash prog mode 0x%02X\n", + __func__, f01_device_status.status_code); + retval = 0; + goto exit; + } + + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of reflash process\n", __func__); +exit: + return retval; +} + int synaptics_fw_updater(unsigned char *fw_data) { int retval; @@ -1431,6 +1529,11 @@ static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, return; } +static void synaptics_rmi4_fwu_work(struct work_struct *work) +{ + fwu_start_reflash(); +} + static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) { int retval; @@ -1497,6 +1600,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) goto exit_free_mem; fwu->initialized = true; + fwu->force_update = FORCE_UPDATE; retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); @@ -1519,6 +1623,13 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) } } +#ifdef INSIDE_FIRMWARE_UPDATE + fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); + INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work); + queue_delayed_work(fwu->fwu_workqueue, + &fwu->fwu_work, + msecs_to_jiffies(1000)); +#endif return 0; exit_remove_attrs: diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c index 85530225abd2..76f9155bd49c 100644 --- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c +++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c @@ -347,28 +347,32 @@ static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, if (rmi4_data->button_0d_enabled == input) return count; - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { - ii = fhandler->intr_reg_num; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; - retval = synaptics_rmi4_i2c_read(rmi4_data, - rmi4_data->f01_ctrl_base_addr + 1 + ii, - &intr_enable, - sizeof(intr_enable)); - if (retval < 0) - return retval; + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; - if (input == 1) - intr_enable |= fhandler->intr_mask; - else - intr_enable &= ~fhandler->intr_mask; + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; - retval = synaptics_rmi4_i2c_write(rmi4_data, - rmi4_data->f01_ctrl_base_addr + 1 + ii, - &intr_enable, - sizeof(intr_enable)); - if (retval < 0) - return retval; + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } } } @@ -637,28 +641,22 @@ static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, finger_status, x, y, wx, wy); -#ifdef TYPE_B_PROTOCOL - input_report_abs(rmi4_data->input_dev, - ABS_MT_POSITION_X, x); - input_report_abs(rmi4_data->input_dev, - ABS_MT_POSITION_Y, y); -#ifdef REPORT_2D_W - input_report_abs(rmi4_data->input_dev, - ABS_MT_TOUCH_MAJOR, max(wx, wy)); - input_report_abs(rmi4_data->input_dev, - ABS_MT_TOUCH_MINOR, min(wx, wy)); -#endif -#else + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); input_report_abs(rmi4_data->input_dev, ABS_MT_POSITION_X, x); input_report_abs(rmi4_data->input_dev, ABS_MT_POSITION_Y, y); + #ifdef REPORT_2D_W input_report_abs(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, max(wx, wy)); input_report_abs(rmi4_data->input_dev, ABS_MT_TOUCH_MINOR, min(wx, wy)); #endif +#ifndef TYPE_B_PROTOCOL input_mt_sync(rmi4_data->input_dev); #endif touch_count++; @@ -853,12 +851,14 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) * Traverse the function handler list and service the source(s) * of the interrupt accordingly. */ - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->num_of_data_sources) { - if (fhandler->intr_mask & - intr[fhandler->intr_reg_num]) { - synaptics_rmi4_report_touch(rmi4_data, - fhandler, &touch_count); + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } } } } @@ -1088,8 +1088,8 @@ static int synaptics_rmi4_capacitance_button_map( if (!pdata->capacitance_button_map) { dev_err(&rmi4_data->i2c_client->dev, - "%s: capacitance_button_map is \ - NULL in board file\n", + "%s: capacitance_button_map is" \ + "NULL in board file\n", __func__); return -ENODEV; } else if (!pdata->capacitance_button_map->map) { @@ -1194,6 +1194,63 @@ static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, return 0; } + + /** + * synaptics_rmi4_query_device_info() + * + * Called by synaptics_rmi4_query_device(). + * + */ +static int synaptics_rmi4_query_device_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char f01_query[F01_STD_QUERY_LEN]; + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + rmi->product_info[0] = f01_query[2] & MASK_7BIT; + rmi->product_info[1] = f01_query[3] & MASK_7BIT; + rmi->date_code[0] = f01_query[4] & MASK_5BIT; + rmi->date_code[1] = f01_query[5] & MASK_4BIT; + rmi->date_code[2] = f01_query[6] & MASK_5BIT; + rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) | + (f01_query[8] & MASK_7BIT); + rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) | + (f01_query[10] & MASK_7BIT); + memcpy(rmi->product_id_string, &f01_query[11], 10); + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found, manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read firmware build id (code %d)\n", + __func__, retval); + return retval; + } + return retval; +} + /** * synaptics_rmi4_query_device() * @@ -1214,7 +1271,6 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) unsigned char page_number; unsigned char intr_count = 0; unsigned char data_sources = 0; - unsigned char f01_query[F01_STD_QUERY_LEN]; unsigned short pdt_entry_addr; unsigned short intr_addr; struct synaptics_rmi4_f01_device_status status; @@ -1264,6 +1320,11 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) rmi4_data->f01_cmd_base_addr = rmi_fd.cmd_base_addr; + retval = + synaptics_rmi4_query_device_info(rmi4_data); + if (retval < 0) + return retval; + retval = synaptics_rmi4_i2c_read(rmi4_data, rmi4_data->f01_data_base_addr, status.data, @@ -1277,7 +1338,17 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) status.status_code); goto flash_prog_mode; } - break; + break; + + case SYNAPTICS_RMI4_F34: + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F11: if (rmi_fd.intr_src_count == 0) break; @@ -1335,56 +1406,24 @@ flash_prog_mode: "%s: Number of interrupt registers = %d\n", __func__, rmi4_data->num_of_intr_regs); - retval = synaptics_rmi4_i2c_read(rmi4_data, - rmi4_data->f01_query_base_addr, - f01_query, - sizeof(f01_query)); - if (retval < 0) - return retval; - - /* RMI Version 4.0 currently supported */ - rmi->version_major = 4; - rmi->version_minor = 0; - - rmi->manufacturer_id = f01_query[0]; - rmi->product_props = f01_query[1]; - rmi->product_info[0] = f01_query[2] & MASK_7BIT; - rmi->product_info[1] = f01_query[3] & MASK_7BIT; - rmi->date_code[0] = f01_query[4] & MASK_5BIT; - rmi->date_code[1] = f01_query[5] & MASK_4BIT; - rmi->date_code[2] = f01_query[6] & MASK_5BIT; - rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) | - (f01_query[8] & MASK_7BIT); - rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) | - (f01_query[10] & MASK_7BIT); - memcpy(rmi->product_id_string, &f01_query[11], 10); - - if (rmi->manufacturer_id != 1) { - dev_err(&rmi4_data->i2c_client->dev, - "%s: Non-Synaptics device found, manufacturer ID = %d\n", - __func__, rmi->manufacturer_id); - } - - retval = synaptics_rmi4_i2c_read(rmi4_data, - rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, - rmi->build_id, - sizeof(rmi->build_id)); - if (retval < 0) - return retval; - memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); /* * Map out the interrupt bit masks for the interrupt sources * from the registered function handlers. */ - list_for_each_entry(fhandler, &rmi->support_fn_list, link) - data_sources += fhandler->num_of_data_sources; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) + data_sources += fhandler->num_of_data_sources; + } if (data_sources) { - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->num_of_data_sources) { - rmi4_data->intr_mask[fhandler->intr_reg_num] |= - fhandler->intr_mask; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, + &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } } } } @@ -1430,12 +1469,14 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) msleep(100); - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) - synaptics_rmi4_f1a_kfree(fhandler); - else - kfree(fhandler->data); - kfree(fhandler); + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } } retval = synaptics_rmi4_query_device(rmi4_data); @@ -1534,12 +1575,14 @@ void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert, exp_fhandler->inserted = false; list_add_tail(&exp_fhandler->link, &exp_fn_list); } else { - list_for_each_entry(exp_fhandler, &exp_fn_list, link) { - if (exp_fhandler->func_init == func_init) { - exp_fhandler->inserted = false; - exp_fhandler->func_init = NULL; - exp_fhandler->func_attn = NULL; - goto exit; + if (!list_empty(&exp_fn_list)) { + list_for_each_entry(exp_fhandler, &exp_fn_list, link) { + if (exp_fhandler->func_init == func_init) { + exp_fhandler->inserted = false; + exp_fhandler->func_init = NULL; + exp_fhandler->func_attn = NULL; + goto exit; + } } } } @@ -1611,7 +1654,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client, retval = -ENOMEM; goto err_input_device; } -/* + if (platform_data->regulator_en) { rmi4_data->regulator = regulator_get(&client->dev, "vdd"); if (IS_ERR(rmi4_data->regulator)) { @@ -1623,7 +1666,7 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client, } regulator_enable(rmi4_data->regulator); } -*/ + rmi4_data->i2c_client = client; rmi4_data->current_page = MASK_8BIT; rmi4_data->board = platform_data; @@ -1652,12 +1695,16 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client, rmi4_data->input_dev->name = DRIVER_NAME; rmi4_data->input_dev->phys = INPUT_PHYS_NAME; rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->id.product = SYNAPTICS_RMI4_DRIVER_PRODUCT; + rmi4_data->input_dev->id.version = SYNAPTICS_RMI4_DRIVER_VERSION; rmi4_data->input_dev->dev.parent = &client->dev; input_set_drvdata(rmi4_data->input_dev, rmi4_data); set_bit(EV_SYN, rmi4_data->input_dev->evbit); set_bit(EV_KEY, rmi4_data->input_dev->evbit); set_bit(EV_ABS, rmi4_data->input_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit); #ifdef INPUT_PROP_DIRECT set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); @@ -1681,9 +1728,11 @@ static int __devinit synaptics_rmi4_probe(struct i2c_client *client, #endif f1a = NULL; - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) - f1a = fhandler->data; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + f1a = fhandler->data; + } } if (f1a) { @@ -1775,16 +1824,17 @@ err_query_device: regulator_put(rmi4_data->regulator); } - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) - synaptics_rmi4_f1a_kfree(fhandler); - else - kfree(fhandler->data); - kfree(fhandler); + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } } -/* + err_regulator: -*/ input_free_device(rmi4_data->input_dev); rmi4_data->input_dev = NULL; @@ -1836,12 +1886,14 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) regulator_put(rmi4_data->regulator); } - list_for_each_entry(fhandler, &rmi->support_fn_list, link) { - if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) - synaptics_rmi4_f1a_kfree(fhandler); - else - kfree(fhandler->data); - kfree(fhandler); + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } } input_free_device(rmi4_data->input_dev); diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h index 7ee0a925959a..ecb9b9415e8a 100644 --- a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h +++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h @@ -20,7 +20,10 @@ #ifndef _SYNAPTICS_DSX_RMI4_H_ #define _SYNAPTICS_DSX_RMI4_H_ -#define SYNAPTICS_RMI4_DRIVER_VERSION "DSX 1.0" +#define SYNAPTICS_RMI4_DS4 0x0001 +#define SYNAPTICS_RMI4_DS5 0x0002 +#define SYNAPTICS_RMI4_DRIVER_PRODUCT SYNAPTICS_RMI4_DS4 +#define SYNAPTICS_RMI4_DRIVER_VERSION 0x1001 #include <linux/version.h> #ifdef CONFIG_HAS_EARLYSUSPEND @@ -158,6 +161,7 @@ struct synaptics_rmi4_device_info { unsigned short serial_number; unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + unsigned char config_id[3]; struct list_head support_fn_list; }; diff --git a/net/wireless/core.c b/net/wireless/core.c index 7426ab13cede..6d3402434a63 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -730,6 +730,36 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } + /* Check that nobody globally advertises any capabilities they do not + * advertise on all possible interface types. + */ + if (wiphy->extended_capabilities_len && + wiphy->num_iftype_ext_capab && + wiphy->iftype_ext_capab) { + u8 supported_on_all, j; + const struct wiphy_iftype_ext_capab *capab; + + capab = wiphy->iftype_ext_capab; + for (j = 0; j < wiphy->extended_capabilities_len; j++) { + if (capab[0].extended_capabilities_len > j) + supported_on_all = + capab[0].extended_capabilities[j]; + else + supported_on_all = 0x00; + for (i = 1; i < wiphy->num_iftype_ext_capab; i++) { + if (j >= capab[i].extended_capabilities_len) { + supported_on_all = 0x00; + break; + } + supported_on_all &= + capab[i].extended_capabilities[j]; + } + if (WARN_ON(wiphy->extended_capabilities[j] & + ~supported_on_all)) + break; + } + } + rdev->wiphy.registered = true; rtnl_unlock(); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ad4b729262fd..30f54d1fc841 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1253,7 +1253,7 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg, struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; - long split_start, band_start, chan_start; + long split_start, band_start, chan_start, capa_start; bool split; }; @@ -1731,6 +1731,47 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.ext_features)) goto nla_put_failure; + state->split_start++; + break; + case 13: + if (rdev->wiphy.num_iftype_ext_capab && + rdev->wiphy.iftype_ext_capab) { + struct nlattr *nested_ext_capab, *nested; + + nested = nla_nest_start(msg, + NL80211_ATTR_IFTYPE_EXT_CAPA); + if (!nested) + goto nla_put_failure; + + for (i = state->capa_start; + i < rdev->wiphy.num_iftype_ext_capab; i++) { + const struct wiphy_iftype_ext_capab *capab; + + capab = &rdev->wiphy.iftype_ext_capab[i]; + + nested_ext_capab = nla_nest_start(msg, i); + if (!nested_ext_capab || + nla_put_u32(msg, NL80211_ATTR_IFTYPE, + capab->iftype) || + nla_put(msg, NL80211_ATTR_EXT_CAPA, + capab->extended_capabilities_len, + capab->extended_capabilities) || + nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK, + capab->extended_capabilities_len, + capab->extended_capabilities_mask)) + goto nla_put_failure; + + nla_nest_end(msg, nested_ext_capab); + if (state->split) + break; + } + nla_nest_end(msg, nested); + if (i < rdev->wiphy.num_iftype_ext_capab) { + state->capa_start = i + 1; + break; + } + } + /* done */ state->split_start = 0; break; |
