summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dp.txt19
-rw-r--r--Documentation/devicetree/bindings/platform/msm/ipa.txt8
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt9
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt13
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c65
-rw-r--r--drivers/scsi/ufs/ufshcd.c3
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c28
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c68
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c66
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c678
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h114
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c104
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c94
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h18
-rw-r--r--include/net/cfg80211.h28
-rw-r--r--include/uapi/linux/nl80211.h30
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_fw_update.c521
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c282
-rw-r--r--kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h6
-rw-r--r--net/wireless/core.c30
-rw-r--r--net/wireless/nl80211.c43
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;