diff options
46 files changed, 1460 insertions, 346 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt index aa227c2628da..707e6edb26ea 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -27,7 +27,46 @@ Required properties - 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. -- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings. +- qcom,aux-cfg0-settings: Specifies the DP AUX configuration 0 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg1-settings: Specifies the DP AUX configuration 1 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg2-settings: Specifies the DP AUX configuration 2 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg3-settings: Specifies the DP AUX configuration 3 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg4-settings: Specifies the DP AUX configuration 4 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg5-settings: Specifies the DP AUX configuration 5 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg6-settings: Specifies the DP AUX configuration 6 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg7-settings: Specifies the DP AUX configuration 7 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg8-settings: Specifies the DP AUX configuration 8 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg9-settings: Specifies the DP AUX configuration 9 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. Optional properties: - qcom,<type>-supply-entries: A node that lists the elements of the supply used by the @@ -87,7 +126,16 @@ Example: "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk"; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <593470>; diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt index efff6c79a9c0..55fde0d4feb6 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt @@ -62,13 +62,6 @@ Required Node Structure rail. This property is applicable only if qcom,ext-pin-ctl property is specified and it is specific to PM660A. -- qcom,force-pd-control - Usage: optional - Value type: <bool> - Definition: Used to enable the pull down control forcibly via SPMI by - disabling the pull down configuration done by hardware - automatically through SWIRE pulses. - - qcom,pbs-client Usage: optional Value type: <phandle> diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 0b60560281c7..c067e8d57b14 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -141,6 +141,7 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ apq8098-v2-qrd.dtb \ apq8098-v2-qrd-skuk-hdk.dtb \ msm8998-v2.1-mtp.dtb \ + msm8998-v2.1-mtp-4k-display.dtb \ msm8998-v2.1-cdp.dtb \ msm8998-v2.1-qrd.dtb \ apq8098-v2.1-mtp.dtb \ diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi index 236565af6af2..679149a78833 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi @@ -417,6 +417,7 @@ #size-cells = <1>; qcom,pmic-revid = <&pm660l_revid>; reg = <0xe000 0x100>; + qcom,pbs-client = <&pm660l_pbs>; label = "oledb"; regulator-name = "regulator-oledb"; @@ -464,6 +465,8 @@ qcom,qpnp-lab-slew-rate = <5000>; qcom,qpnp-lab-init-voltage = <4600000>; qcom,qpnp-lab-init-amoled-voltage = <4600000>; + + qcom,notify-lab-vreg-ok-sts; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index 24186aca22be..2b9e13ea24f2 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -502,7 +502,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,core-supply-entries { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts new file mode 100644 index 000000000000..7d537aa35533 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts @@ -0,0 +1,51 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP, 4k display"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 4>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi index 4cd8bf4407ac..19862f02aa84 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi @@ -147,7 +147,13 @@ 23 1e 07 08 05 03 04 a0 23 18 07 08 04 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; }; &dsi_dual_nt36850_truly_cmd { @@ -195,7 +201,13 @@ 20 12 05 06 03 13 04 a0]; qcom,config-select = <&dsi_nt35597_truly_dsc_cmd_config2>; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-panel-on-check-value = <0x9c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; }; &dsi_dual_nt35597_video { diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi index b263d2a68792..787c4f1e2fb6 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi @@ -505,7 +505,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03]; + qcom,aux-cfg0-settings = [20 00]; + qcom,aux-cfg1-settings = [24 13 23 1d]; + qcom,aux-cfg2-settings = [28 00]; + qcom,aux-cfg3-settings = [2c 00]; + qcom,aux-cfg4-settings = [30 0a]; + qcom,aux-cfg5-settings = [34 28]; + qcom,aux-cfg6-settings = [38 0a]; + qcom,aux-cfg7-settings = [3c 03]; + qcom,aux-cfg8-settings = [40 b7]; + qcom,aux-cfg9-settings = [44 03]; qcom,logical2physical-lane-map = [00 01 02 03]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <300000>; diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 4b0918e2136d..2f468dfe1c5a 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -186,6 +186,7 @@ CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_IPTABLES_128=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index caa140651102..fdf8deacee17 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -186,6 +186,7 @@ CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_IPTABLES_128=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index 19956f030ae9..adb07cdb7e8d 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -928,7 +928,8 @@ static unsigned long branch_clk_get_rate(struct clk *c) { struct branch_clk *branch = to_branch_clk(c); - if (branch->max_div) + if (branch->max_div || + (branch->aggr_sibling_rates && !branch->is_prepared)) return branch->c.rate; return clk_get_rate(c->parent); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 51159711b1d8..25fe6c85a34e 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -3288,6 +3288,43 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain, 1 << DOMAIN_ATTR_ENABLE_TTBR1; ret = 0; break; + case DOMAIN_ATTR_GEOMETRY: { + struct iommu_domain_geometry *geometry = + (struct iommu_domain_geometry *)data; + + if (smmu_domain->smmu != NULL) { + dev_err(smmu_domain->smmu->dev, + "cannot set geometry attribute while attached\n"); + ret = -EBUSY; + break; + } + + if (geometry->aperture_start >= SZ_1G * 4ULL || + geometry->aperture_end >= SZ_1G * 4ULL) { + pr_err("fastmap does not support IOVAs >= 4GB\n"); + ret = -EINVAL; + break; + } + if (smmu_domain->attributes + & (1 << DOMAIN_ATTR_GEOMETRY)) { + if (geometry->aperture_start + < domain->geometry.aperture_start) + domain->geometry.aperture_start = + geometry->aperture_start; + + if (geometry->aperture_end + > domain->geometry.aperture_end) + domain->geometry.aperture_end = + geometry->aperture_end; + } else { + smmu_domain->attributes |= 1 << DOMAIN_ATTR_GEOMETRY; + domain->geometry.aperture_start = + geometry->aperture_start; + domain->geometry.aperture_end = geometry->aperture_end; + } + ret = 0; + break; + } default: ret = -ENODEV; break; diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 8c6364f03eac..0881d68f34d8 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -188,7 +188,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping, iommu_tlbiall(mapping->domain); mapping->have_stale_tlbs = false; - av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base, + av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + mapping->base, mapping->base + mapping->size - 1, skip_sync); } @@ -367,7 +369,8 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page, if (unlikely(iova == DMA_ERROR_CODE)) goto fail; - pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova); + pmd = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, iova); if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot))) goto fail_free_iova; @@ -391,7 +394,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; unsigned long flags; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; size_t len = ALIGN(size + offset, FAST_PAGE_SIZE); int nptes = len >> FAST_PAGE_SHIFT; @@ -414,7 +418,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev, { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -427,7 +432,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev, { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -555,8 +561,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, while (sg_miter_next(&miter)) { int nptes = miter.length >> FAST_PAGE_SHIFT; - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, - iova_iter); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + iova_iter); if (unlikely(av8l_fast_map_public( ptep, page_to_phys(miter.page), miter.length, prot))) { @@ -584,7 +591,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, out_unmap: /* need to take the lock again for page tables and iova */ spin_lock_irqsave(&mapping->lock, flags); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + dma_addr); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); out_free_iova: @@ -616,7 +625,8 @@ static void fast_smmu_free(struct device *dev, size_t size, pages = area->pages; dma_common_free_remap(vaddr, size, VM_USERMAP, false); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, dma_handle); spin_lock_irqsave(&mapping->lock, flags); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); @@ -720,7 +730,7 @@ static const struct dma_map_ops fast_smmu_dma_ops = { * * Creates a mapping structure which holds information about used/unused IO * address ranges, which is required to perform mapping with IOMMU aware - * functions. The only VA range supported is [0, 4GB). + * functions. The only VA range supported is [0, 4GB]. * * The client device need to be attached to the mapping with * fast_smmu_attach_device function. @@ -774,6 +784,7 @@ int fast_smmu_attach_device(struct device *dev, struct iommu_domain *domain = mapping->domain; struct iommu_pgtbl_info info; u64 size = (u64)mapping->bits << PAGE_SHIFT; + struct iommu_domain_geometry geometry; if (mapping->base + size > (SZ_1G * 4ULL)) return -EINVAL; @@ -788,8 +799,11 @@ int fast_smmu_attach_device(struct device *dev, mapping->fast->domain = domain; mapping->fast->dev = dev; - domain->geometry.aperture_start = mapping->base; - domain->geometry.aperture_end = mapping->base + size - 1; + geometry.aperture_start = mapping->base; + geometry.aperture_end = mapping->base + size - 1; + if (iommu_domain_set_attr(domain, DOMAIN_ATTR_GEOMETRY, + &geometry)) + return -EINVAL; if (iommu_attach_device(domain, dev)) return -EINVAL; diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index 3582e206db68..5378e95c4627 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -133,6 +133,9 @@ struct av8l_fast_io_pgtable { #define AV8L_FAST_TCR_EPD1_SHIFT 23 #define AV8L_FAST_TCR_EPD1_FAULT 1 +#define AV8L_FAST_TCR_SEP_SHIFT (15 + 32) +#define AV8L_FAST_TCR_SEP_UPSTREAM 7ULL + #define AV8L_FAST_MAIR_ATTR_SHIFT(n) ((n) << 3) #define AV8L_FAST_MAIR_ATTR_MASK 0xff #define AV8L_FAST_MAIR_ATTR_DEVICE 0x04 @@ -173,12 +176,12 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep) } void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base, - u64 end, bool skip_sync) + u64 start, u64 end, bool skip_sync) { int i; - av8l_fast_iopte *pmdp = pmds; + av8l_fast_iopte *pmdp = iopte_pmd_offset(pmds, base, start); - for (i = base >> AV8L_FAST_PAGE_SHIFT; + for (i = start >> AV8L_FAST_PAGE_SHIFT; i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) { if (!(*pmdp & AV8L_FAST_PTE_VALID)) { *pmdp = 0; @@ -256,16 +259,17 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size) __av8l_fast_unmap(ptep, size, true); } -/* upper layer must take care of TLB invalidation */ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); + struct io_pgtable *iop = &data->iop; av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT; __av8l_fast_unmap(ptep, size, false); dmac_clean_range(ptep, ptep + nptes); + iop->cfg.tlb->tlb_flush_all(iop->cookie); return size; } @@ -522,6 +526,7 @@ av8l_fast_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) #if defined(CONFIG_ARM) reg |= ARM_32_LPAE_TCR_EAE; #endif + reg |= AV8L_FAST_TCR_SEP_UPSTREAM << AV8L_FAST_TCR_SEP_SHIFT; cfg->av8l_fast_cfg.tcr = reg; /* MAIRs */ @@ -668,7 +673,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 8K map calls */ for (iova = base; iova < max; iova += SZ_8K) { @@ -689,7 +694,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 16K map calls */ for (iova = base; iova < max; iova += SZ_16K) { @@ -710,7 +715,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 64K map calls */ for (iova = base; iova < max; iova += SZ_64K) { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 5f1c9c2f9436..63e46125c292 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -3432,22 +3432,21 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, /* * If frame_id = 1 then no eof check is needed */ - if (vfe_dev->axi_data.src_info[VFE_PIX_0].active && - vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) { + if (vfe_dev->axi_data.src_info[frame_src].active && + frame_src == VFE_PIX_0 && + vfe_dev->axi_data.src_info[frame_src].accept_frame == false) { pr_debug("%s:%d invalid time to request frame %d\n", __func__, __LINE__, frame_id); goto error; } - if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id != - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev-> - axi_data.src_info[VFE_PIX_0].sof_counter_step)) || - ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id != + if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id != vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> - axi_data.src_info[frame_src].sof_counter_step))) { + axi_data.src_info[VFE_PIX_0].sof_counter_step)) || + ((!vfe_dev->axi_data.src_info[frame_src].active))) { pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", __func__, __LINE__, frame_id, - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, - vfe_dev->axi_data.src_info[VFE_PIX_0].active); + vfe_dev->axi_data.src_info[frame_src].frame_id, + vfe_dev->axi_data.src_info[frame_src].active); goto error; } if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { @@ -3954,6 +3953,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->req_frm_ver2; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(req_frm->stream_handle)); + if (stream_info == NULL) { + pr_err_ratelimited("%s: stream_info is NULL\n", + __func__); + rc = -EINVAL; + break; + } rc = msm_isp_request_frame(vfe_dev, stream_info, req_frm->user_stream_id, req_frm->frame_id, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index 65009cb22286..a8d4cfb43927 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -141,6 +141,11 @@ static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data( struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data; struct msm_vfe_axi_stream *stream_info; + if (stream_idx >= VFE_AXI_SRC_MAX) { + pr_err("invalid stream_idx %d\n", stream_idx); + return NULL; + } + if (vfe_dev->is_split && stream_idx < RDI_INTF_0) stream_info = &common_data->streams[stream_idx]; else diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index b067c4916341..0ff270bb8410 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -201,6 +201,7 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; uint32_t read_val = 0; uint32_t reg_offset = master * 0x200 + queue * 0x100; read_val = msm_camera_io_r_mb(cci_dev->base + @@ -223,6 +224,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); reg_val = 1 << ((master * 2) + queue); CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__); + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); msm_camera_io_w_mb(reg_val, cci_dev->base + @@ -230,6 +233,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, CDBG("%s line %d wait_for_completion_timeout\n", __func__, __LINE__); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = wait_for_completion_timeout(&cci_dev-> cci_master_info[master].report_q[queue], CCI_TIMEOUT); if (rc <= 0) { @@ -438,10 +443,17 @@ static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev, enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) { + unsigned long flags; uint32_t reg_val = 1 << ((master * 2) + queue); msm_cci_load_report_cmd(cci_dev, master, queue); + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); + msm_camera_io_w_mb(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR); return msm_cci_wait(cci_dev, master, queue); @@ -451,13 +463,19 @@ static void msm_cci_process_half_q(struct cci_device *cci_dev, enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) { + unsigned long flags; uint32_t reg_val = 1 << ((master * 2) + queue); + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { msm_cci_load_report_cmd(cci_dev, master, queue); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); msm_camera_io_w_mb(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); } static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, @@ -465,15 +483,23 @@ static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); return rc; } } else { + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait_report_cmd(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); @@ -501,8 +527,13 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_lock_queue(cci_dev, master, queue, 0); if (rc < 0) { pr_err("%s failed line %d\n", __func__, __LINE__); @@ -516,6 +547,8 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev, } else { atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); @@ -570,6 +603,7 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev, uint32_t reg_offset; uint32_t val = 0; uint32_t max_queue_size; + unsigned long flags; if (i2c_cmd == NULL) { pr_err("%s:%d Failed line\n", __func__, @@ -613,7 +647,11 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev, msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + reg_offset); + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); max_queue_size = cci_dev->cci_i2c_queue_info[master][queue]. max_queue_size; @@ -1641,6 +1679,7 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, static irqreturn_t msm_cci_irq(int irq_num, void *data) { uint32_t irq; + unsigned long flags; struct cci_device *cci_dev = data; irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR); msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR); @@ -1667,22 +1706,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data) if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_0], flags); atomic_set(&cci_master_info->q_free[QUEUE_0], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { complete(&cci_master_info->report_q[QUEUE_0]); atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_0], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_1], flags); atomic_set(&cci_master_info->q_free[QUEUE_1], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { complete(&cci_master_info->report_q[QUEUE_1]); atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_1], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) { cci_dev->cci_master_info[MASTER_1].status = 0; @@ -1691,22 +1738,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data) if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_0], flags); atomic_set(&cci_master_info->q_free[QUEUE_0], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { complete(&cci_master_info->report_q[QUEUE_0]); atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_0], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_1], flags); atomic_set(&cci_master_info->q_free[QUEUE_1], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { complete(&cci_master_info->report_q[QUEUE_1]); atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_1], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) { cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; @@ -1795,7 +1850,9 @@ static void msm_cci_init_cci_params(struct cci_device *new_cci_dev) mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]); init_completion(&new_cci_dev-> cci_master_info[i].report_q[j]); - } + spin_lock_init(&new_cci_dev-> + cci_master_info[i].lock_q[j]); + } } return; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h index 6e39d814bd73..eb615cc7a62c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -125,6 +125,7 @@ struct msm_camera_cci_master_info { struct mutex mutex_q[NUM_QUEUES]; struct completion report_q[NUM_QUEUES]; atomic_t done_pending[NUM_QUEUES]; + spinlock_t lock_q[NUM_QUEUES]; }; struct msm_cci_clk_params_t { diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 3f9eeabc5464..9db2871e8150 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -192,6 +192,8 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) #define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define SHOW_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x\n" +#define WCNSS_USER_MAC_ADDR_LENGTH 18 /* message types */ #define WCNSS_CTRL_MSG_START 0x01000000 @@ -427,23 +429,28 @@ static struct { static ssize_t wcnss_wlan_macaddr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char macAddr[WLAN_MAC_ADDR_SIZE]; + int index; + int macAddr[WLAN_MAC_ADDR_SIZE]; if (!penv) return -ENODEV; - pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf); + if (strlen(buf) != WCNSS_USER_MAC_ADDR_LENGTH) { + dev_err(dev, "%s: Invalid MAC addr length\n", __func__); + return -EINVAL; + } if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR, - (int *)&macAddr[0], (int *)&macAddr[1], - (int *)&macAddr[2], (int *)&macAddr[3], - (int *)&macAddr[4], (int *)&macAddr[5])) { - + &macAddr[0], &macAddr[1], &macAddr[2], + &macAddr[3], &macAddr[4], &macAddr[5])) { pr_err("%s: Failed to Copy MAC\n", __func__); return -EINVAL; } - memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr)); + for (index = 0; index < WLAN_MAC_ADDR_SIZE; index++) { + memcpy(&penv->wlan_nv_macAddr[index], + (char *)&macAddr[index], sizeof(char)); + } pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__, penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], @@ -459,7 +466,7 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev, if (!penv) return -ENODEV; - return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR, + return scnprintf(buf, PAGE_SIZE, SHOW_MAC_ADDRESS_STR, penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 4c75b4d392c6..39400dda27c2 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -367,6 +367,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) return err; } + armpmu->pmu_state = ARM_PMU_STATE_RUNNING; + return 0; } @@ -601,10 +603,12 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) struct platform_device *pmu_device = cpu_pmu->plat_device; struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events; + cpu_pmu->pmu_state = ARM_PMU_STATE_GOING_DOWN; + irqs = min(pmu_device->num_resources, num_possible_cpus()); irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { + if (irq > 0 && irq_is_percpu(irq)) { on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); free_percpu_irq(irq, &hw_events->percpu_pmu); } else { @@ -617,10 +621,11 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs)) continue; irq = platform_get_irq(pmu_device, i); - if (irq >= 0) + if (irq > 0) free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu)); } } + cpu_pmu->pmu_state = ARM_PMU_STATE_OFF; } static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) @@ -639,7 +644,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) } irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { + if (irq > 0 && irq_is_percpu(irq)) { err = request_percpu_irq(irq, handler, "arm-pmu", &hw_events->percpu_pmu); if (err) { @@ -648,6 +653,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) return err; } on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1); + cpu_pmu->percpu_irq = irq; } else { for (i = 0; i < irqs; ++i) { int cpu = i; @@ -754,13 +760,6 @@ static void cpu_pm_pmu_common(void *info) return; } - /* - * Always reset the PMU registers on power-up even if - * there are no events running. - */ - if (cmd == CPU_PM_EXIT && armpmu->reset) - armpmu->reset(armpmu); - if (!enabled) { data->ret = NOTIFY_OK; return; @@ -795,6 +794,13 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, .cpu = smp_processor_id(), }; + /* + * Always reset the PMU registers on power-up even if + * there are no events running. + */ + if (cmd == CPU_PM_EXIT && data.armpmu->reset) + data.armpmu->reset(data.armpmu); + cpu_pm_pmu_common(&data); return data.ret; } @@ -824,6 +830,7 @@ static inline void cpu_pm_pmu_common(void *info) { } static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, void *hcpu) { + int irq = -1; unsigned long masked_action = (action & ~CPU_TASKS_FROZEN); struct cpu_pm_pmu_args data = { .armpmu = container_of(b, struct arm_pmu, hotplug_nb), @@ -835,37 +842,37 @@ static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, switch (masked_action) { case CPU_STARTING: - data.cmd = CPU_PM_EXIT; - break; - case CPU_DYING: - data.cmd = CPU_PM_ENTER; - break; case CPU_DOWN_FAILED: - data.cmd = CPU_PM_ENTER_FAILED; - break; - case CPU_ONLINE: - if (data.armpmu->plat_device) { - struct platform_device *pmu_device = - data.armpmu->plat_device; - int irq = platform_get_irq(pmu_device, 0); - - if (irq >= 0 && irq_is_percpu(irq)) { - smp_call_function_single(data.cpu, - cpu_pmu_enable_percpu_irq, &irq, 1); - } + /* + * Always reset the PMU registers on power-up even if + * there are no events running. + */ + if (data.armpmu->reset) + data.armpmu->reset(data.armpmu); + if (data.armpmu->pmu_state == ARM_PMU_STATE_RUNNING) { + if (data.armpmu->plat_device) + irq = data.armpmu->percpu_irq; + /* Arm the PMU IRQ before appearing. */ + if (irq > 0 && irq_is_percpu(irq)) + cpu_pmu_enable_percpu_irq(&irq); + data.cmd = CPU_PM_EXIT; + cpu_pm_pmu_common(&data); } - return NOTIFY_DONE; + return NOTIFY_OK; + case CPU_DYING: + if (data.armpmu->pmu_state != ARM_PMU_STATE_OFF) { + data.cmd = CPU_PM_ENTER; + cpu_pm_pmu_common(&data); + /* Disarm the PMU IRQ before disappearing. */ + if (data.armpmu->plat_device) + irq = data.armpmu->percpu_irq; + if (irq > 0 && irq_is_percpu(irq)) + cpu_pmu_disable_percpu_irq(&irq); + } + return NOTIFY_OK; default: return NOTIFY_DONE; } - - if (smp_processor_id() == data.cpu) - cpu_pm_pmu_common(&data); - else - smp_call_function_single(data.cpu, - cpu_pm_pmu_common, &data, 1); - - return data.ret; } static int cpu_pmu_init(struct arm_pmu *cpu_pmu) @@ -966,7 +973,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) /* Check the IRQ type and prohibit a mix of PPIs and SPIs */ irq = platform_get_irq(pdev, i); - if (irq >= 0) { + if (irq > 0) { bool spi = !irq_is_percpu(irq); if (i > 0 && spi != using_spi) { @@ -1085,6 +1092,9 @@ int arm_pmu_device_probe(struct platform_device *pdev, if (ret) goto out_destroy; + pmu->pmu_state = ARM_PMU_STATE_OFF; + pmu->percpu_irq = -1; + pr_info("enabled with %s PMU driver, %d counters available\n", pmu->name, pmu->num_events); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 7aaddbbdabaf..5ee6e5d2d9e3 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -2276,6 +2276,36 @@ static int ipa3_q6_set_ex_path_to_apps(void) desc[num_descs].len = cmd_pyld->len; num_descs++; } + + /* disable statuses for modem producers */ + if (IPA_CLIENT_IS_Q6_PROD(client_idx)) { + ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes); + + reg_write.skip_pipeline_clear = false; + reg_write.pipeline_clear_options = + IPAHAL_HPS_CLEAR; + reg_write.offset = + ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n, + ep_idx); + reg_write.value = 0; + reg_write.value_mask = ~0; + cmd_pyld = ipahal_construct_imm_cmd( + IPA_IMM_CMD_REGISTER_WRITE, ®_write, false); + if (!cmd_pyld) { + IPAERR("fail construct register_write cmd\n"); + ipa_assert(); + return -EFAULT; + } + + desc[num_descs].opcode = ipahal_imm_cmd_get_opcode( + IPA_IMM_CMD_REGISTER_WRITE); + desc[num_descs].type = IPA_IMM_CMD_DESC; + desc[num_descs].callback = ipa3_destroy_imm; + desc[num_descs].user1 = cmd_pyld; + desc[num_descs].pyld = cmd_pyld->data; + desc[num_descs].len = cmd_pyld->len; + num_descs++; + } } /* Will wait 500msecs for IPA tag process completion */ diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index c6009d767db5..2421cd07acc3 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -643,6 +643,9 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) int rc; int force_pt_coherent = 1; int smmu_bypass = !ctx->smmu_s1_en; + dma_addr_t iova_base = 0; + dma_addr_t iova_end = ctx->smmu_base + ctx->smmu_size - 1; + struct iommu_domain_geometry geometry; if (!ctx->use_smmu) return 0; @@ -700,6 +703,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) rc); goto release_mapping; } + memset(&geometry, 0, sizeof(geometry)); + geometry.aperture_start = iova_base; + geometry.aperture_end = iova_end; + rc = iommu_domain_set_attr(ctx->mapping->domain, + DOMAIN_ATTR_GEOMETRY, + &geometry); + if (rc) { + dev_err(ctx->dev, "Set geometry attribute to SMMU failed (%d)\n", + rc); + goto release_mapping; + } } } diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c index 8582f915eb5e..bee9a3d82eeb 100644 --- a/drivers/regulator/qpnp-oledb-regulator.c +++ b/drivers/regulator/qpnp-oledb-regulator.c @@ -377,12 +377,19 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) } if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) { - rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, - trigger_bitmap); + rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + + OLEDB_SPARE_CTL, + OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); if (rc < 0) { - pr_err("Failed to trigger the PBS sequence\n"); + pr_err("Failed to write SPARE_CTL rc=%d\n", rc); return rc; } + + rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, + trigger_bitmap); + if (rc < 0) + pr_err("Failed to trigger the PBS sequence\n"); + pr_debug("PBS event triggered\n"); } else { pr_debug("OLEDB_SPARE_CTL register bit not set\n"); @@ -1123,8 +1130,14 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) oledb->pbs_control = of_property_read_bool(of_node, "qcom,pbs-control"); - oledb->force_pd_control = - of_property_read_bool(of_node, "qcom,force-pd-control"); + /* Use the force_pd_control only for PM660A versions <= v2.0 */ + if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && + oledb->pmic_rev_id->rev4 <= PM660L_V2P0_REV4) { + if (!(oledb->pmic_rev_id->rev4 == PM660L_V2P0_REV4 && + oledb->pmic_rev_id->rev2 > PM660L_V2P0_REV2)) { + oledb->force_pd_control = true; + } + } if (oledb->force_pd_control) { oledb->pbs_dev_node = of_parse_phandle(of_node, @@ -1222,13 +1235,6 @@ static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb) int rc = 0; u8 val; - rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + - OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); - if (rc < 0) { - pr_err("Failed to write SPARE_CTL rc=%d\n", rc); - return rc; - } - val = 1; rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL, &val, 1); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 79bb3337ba36..c5393d517432 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -9238,10 +9238,11 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up) /* scale up to G3 now */ new_pwr_info.gear_tx = UFS_HS_G3; new_pwr_info.gear_rx = UFS_HS_G3; - ret = ufshcd_change_power_mode(hba, &new_pwr_info); - if (ret) - goto out; + /* now, fall through to set the HS-G3 */ } + ret = ufshcd_change_power_mode(hba, &new_pwr_info); + if (ret) + goto out; } else { memcpy(&new_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index e60869144339..0ecf1ef92ab3 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -59,11 +59,13 @@ static int panel_debug_base_open(struct inode *inode, struct file *file) static int panel_debug_base_release(struct inode *inode, struct file *file) { struct mdss_debug_base *dbg = file->private_data; + mutex_lock(&mdss_debug_lock); if (dbg && dbg->buf) { kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; } + mutex_unlock(&mdss_debug_lock); return 0; } @@ -385,11 +387,13 @@ static int mdss_debug_base_open(struct inode *inode, struct file *file) static int mdss_debug_base_release(struct inode *inode, struct file *file) { struct mdss_debug_base *dbg = file->private_data; + mutex_lock(&mdss_debug_lock); if (dbg && dbg->buf) { kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; } + mutex_unlock(&mdss_debug_lock); return 0; } diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 7a1b563fbb6c..1e878e9a00cb 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -69,6 +69,11 @@ static int mdss_dp_process_phy_test_pattern_request( static int mdss_dp_send_audio_notification( struct mdss_dp_drv_pdata *dp, int val); +static inline void mdss_dp_reset_sink_count(struct mdss_dp_drv_pdata *dp) +{ + memset(&dp->sink_count, 0, sizeof(dp->sink_count)); +} + static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; @@ -133,22 +138,77 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) return !strncmp(clk_name, clk_prefix, strlen(clk_prefix)); } +static void mdss_dp_reset_phy_config_indices(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i].current_index = 0; +} + +static void mdss_dp_phy_aux_cfg_reset(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i] = (const struct mdss_dp_phy_cfg){ 0 }; +} + +static int mdss_dp_parse_aux_cfg(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + int len = 0, i = 0, j = 0, config_count = 0; + const char *data; + int const minimum_config_count = 1; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + const char *property = mdss_dp_get_phy_aux_config_property(i); + + data = of_get_property(pdev->dev.of_node, property, &len); + if (!data) { + pr_err("Unable to read %s\n", property); + goto error; + } + + config_count = len - 1; + if ((config_count < minimum_config_count) || + (config_count > MDSS_DP_MAX_PHY_CFG_VALUE_CNT)) { + pr_err("Invalid config count (%d) configs for %s\n", + config_count, property); + goto error; + } + + dp->aux_cfg[i].offset = data[0]; + dp->aux_cfg[i].cfg_cnt = config_count; + pr_debug("%s offset=0x%x, cfg_cnt=%d\n", + property, + dp->aux_cfg[i].offset, + dp->aux_cfg[i].cfg_cnt); + for (j = 1; j < len; j++) { + dp->aux_cfg[i].lut[j - 1] = data[j]; + pr_debug("%s lut[%d]=0x%x\n", + property, + i, + dp->aux_cfg[i].lut[j - 1]); + } + } + + return 0; + +error: + mdss_dp_phy_aux_cfg_reset(dp); + return -EINVAL; +} + static int mdss_dp_parse_prop(struct platform_device *pdev, struct mdss_dp_drv_pdata *dp_drv) { int len = 0, i = 0, rc = 0; const char *data; - data = of_get_property(pdev->dev.of_node, - "qcom,aux-cfg-settings", &len); - if ((!data) || (len != AUX_CFG_LEN)) { - pr_err("%s:%d, Unable to read DP AUX CFG settings", - __func__, __LINE__); - return -EINVAL; - } - - for (i = 0; i < len; i++) - dp_drv->aux_cfg[i] = data[i]; + rc = mdss_dp_parse_aux_cfg(pdev, dp_drv); + if (rc) + return rc; data = of_get_property(pdev->dev.of_node, "qcom,logical2physical-lane-map", &len); @@ -958,6 +1018,12 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp) mdss_dp_configuration_ctrl(&dp->ctrl_io, data); } +static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) +{ + if (dp && dp->ext_audio_data.intf_ops.notify) + dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); +} + static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) { int ret = 0; @@ -989,26 +1055,28 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp, bool connected) { - mutex_lock(&dp->pd_msg_mutex); + mutex_lock(&dp->attention_lock); pr_debug("cable_connected to %d\n", connected); if (dp->cable_connected != connected) dp->cable_connected = connected; else pr_debug("no change in cable status\n"); - mutex_unlock(&dp->pd_msg_mutex); + mutex_unlock(&dp->attention_lock); } static int dp_get_cable_status(struct platform_device *pdev, u32 vote) { - struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); + struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev); u32 hpd; - if (!dp_ctrl) { + if (!dp) { DEV_ERR("%s: invalid input\n", __func__); return -ENODEV; } - hpd = dp_ctrl->cable_connected; + mutex_lock(&dp->attention_lock); + hpd = dp->cable_connected; + mutex_unlock(&dp->attention_lock); return hpd; } @@ -1230,12 +1298,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic) return 0; } /* dp_init_panel_info */ -static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) -{ - if (dp && dp->ext_audio_data.intf_ops.notify) - dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); -} - /** * mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation * @orientation: usb plug orientation @@ -1621,15 +1683,19 @@ int mdss_dp_on(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); - if (dp_drv->power_on) { - /* - * Acknowledge the connection event if link training has already - * been done. This will unblock the external display thread and - * allow the driver to progress. For example, in the case of - * video test pattern requests, to send the test response and - * start transmitting the test pattern. - */ - mdss_dp_ack_state(dp_drv, true); + /* + * If the link already active, then nothing needs to be done here. + * However, it is possible that the the power_on flag could be + * set to true but we would still need to initialize the DP host. + * An example of this use-case is when a multiport dongle is connected + * and subsequently the downstream sink is disconnected. This would + * only go through the IRQ HPD path where we tear down the link but + * the power_on flag remains set to true. When the downstream sink + * is subsequently connected again, we need to re-initialize DP + * host + */ + if (dp_drv->power_on && + (dp_drv->new_vic && (dp_drv->new_vic == dp_drv->vic))) { pr_debug("Link already setup, return\n"); return 0; } @@ -1647,6 +1713,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata) return mdss_dp_on_hpd(dp_drv); } +static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp) +{ + return dp->dpcd.downstream_port.dfp_present; +} + +static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge(dp) && + (dp->sink_count.count == 0)); +} + +static bool mdss_dp_is_ds_bridge_no_local_edid(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge_sink_count_zero(dp) && + !(dp->dpcd.flags & DPCD_PORT_0_EDID_PRESENTED)); +} + static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) { if (!dp_drv->power_on) { @@ -1664,10 +1747,16 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) /* Make sure DP mainlink and audio engines are disabled */ wmb(); - mdss_dp_ack_state(dp_drv, false); + /* + * If downstream device is a brige which no longer has any + * downstream devices connected to it, then we should reset + * the current panel info + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp_drv)) + dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); + mutex_unlock(&dp_drv->train_mutex); - complete_all(&dp_drv->irq_comp); pr_debug("end\n"); return 0; @@ -1694,11 +1783,11 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_host_deinit(dp_drv); dp_drv->power_on = false; - dp_drv->sink_info_read = false; dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); - mdss_dp_ack_state(dp_drv, false); mdss_dp_reset_test_data(dp_drv); + mdss_dp_reset_sink_count(dp_drv); + dp_drv->prev_sink_count = dp_drv->sink_count; mutex_unlock(&dp_drv->train_mutex); pr_debug("DP off done\n"); @@ -1737,8 +1826,9 @@ static int mdss_dp_send_audio_notification( if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) { dp->audio_test_req = false; - pr_debug("sending audio notification\n"); flags |= MSM_EXT_DISP_HPD_AUDIO; + pr_debug("sending audio notification = %d, flags = %d\n", val, + flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1763,7 +1853,8 @@ static int mdss_dp_send_video_notification( goto end; } - flags |= MSM_EXT_DISP_HPD_VIDEO; + flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO; + pr_debug("sending video notification = %d, flags = %d\n", val, flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1820,6 +1911,8 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata) dp_drv->edid_buf = edid_init_data.buf; dp_drv->edid_buf_size = edid_init_data.buf_size; + mdss_dp_set_default_resolution(dp_drv); + return 0; } @@ -1871,14 +1964,16 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_ctrl_reset(&dp_drv->ctrl_io); mdss_dp_phy_reset(&dp_drv->ctrl_io); mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_aux_set_limits(&dp_drv->ctrl_io); + 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)); - mdss_dp_phy_aux_setup(&dp_drv->phy_io, dp_drv->aux_cfg, - dp_drv->phy_reg_offset); + mdss_dp_reset_phy_config_indices(dp_drv); + mdss_dp_phy_aux_setup(dp_drv); mdss_dp_irq_enable(dp_drv); dp_drv->dp_initialized = true; @@ -1946,10 +2041,11 @@ static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp) static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, enum notification_status status) { - const int irq_comp_timeout = HZ * 2; int ret = 0; + bool notify = false; + bool connect; - mutex_lock(&dp->pd_msg_mutex); + pr_debug("beginning notification\n"); if (status == dp->hpd_notification_status) { pr_debug("No change in status %s --> %s\n", mdss_dp_notification_status_to_string(status), @@ -1962,39 +2058,40 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, case NOTIFY_CONNECT_IRQ_HPD: if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD) goto invalid_request; - /* Follow the same programming as for NOTIFY_CONNECT */ - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_CONNECT: - if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) || - (dp->hpd_notification_status == - NOTIFY_DISCONNECT_IRQ_HPD)) + if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) goto invalid_request; - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_DISCONNECT: - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); + /* + * Userspace triggers a disconnect event on boot up, this must + * not be processed as there was no previously connected sink + * device. + */ + if (dp->hpd_notification_status == NOTIFY_UNKNOWN) + goto invalid_request; + if (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD) { + /* + * user modules already turned off. Need to explicitly + * turn off DP core here. + */ + mdss_dp_off_hpd(dp); + } else { + notify = true; + connect = false; + } break; case NOTIFY_DISCONNECT_IRQ_HPD: if (dp->hpd_notification_status == NOTIFY_DISCONNECT) goto invalid_request; - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); - if (!IS_ERR_VALUE(ret) && ret) { - reinit_completion(&dp->irq_comp); - ret = wait_for_completion_timeout(&dp->irq_comp, - irq_comp_timeout); - if (ret <= 0) { - pr_warn("irq_comp timed out\n"); - ret = -EINVAL; - } else { - ret = 0; - } - } + notify = true; + connect = false; break; default: pr_err("Invalid notification status = %d\n", status); @@ -2002,7 +2099,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, break; } - goto end; + goto notify; invalid_request: pr_err("Invalid request %s --> %s\n", @@ -2011,16 +2108,34 @@ invalid_request: mdss_dp_notification_status_to_string(status)); ret = -EINVAL; -end: +notify: + if (ret || !notify) { + pr_debug("not sending notification\n"); + goto end; + } + + atomic_set(&dp->notification_pending, 1); + if (connect) { + mdss_dp_host_init(&dp->panel_data); + ret = mdss_dp_send_video_notification(dp, true); + } else { + mdss_dp_send_audio_notification(dp, false); + ret = mdss_dp_send_video_notification(dp, false); + } + if (!ret) { pr_debug("Successfully sent notification %s --> %s\n", mdss_dp_notification_status_to_string( dp->hpd_notification_status), mdss_dp_notification_status_to_string(status)); - dp->hpd_notification_status = status; + } else { + pr_err("%s Notification failed\n", + mdss_dp_notification_status_to_string(status)); + atomic_set(&dp->notification_pending, 0); } - mutex_unlock(&dp->pd_msg_mutex); +end: + dp->hpd_notification_status = status; return ret; } @@ -2029,9 +2144,6 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) int ret; u32 max_pclk_khz; - if (dp->sink_info_read) - return 0; - pr_debug("start\n"); ret = mdss_dp_dpcd_cap_read(dp); @@ -2044,8 +2156,25 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) */ pr_err("dpcd read failed, set failsafe parameters\n"); mdss_dp_set_default_link_parameters(dp); + goto read_edid; } + /* + * When connected to a multiport adaptor which does not have a + * local EDID present, do not attempt to read the EDID. + * When connected to a multiport adaptor with no downstream device + * connected to it, do not attempt to read the EDID. It is possible + * that the adaptor may advertise the presence of local EDID, but it + * is not guaranteed to work. + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + if (mdss_dp_is_ds_bridge_no_local_edid(dp)) + pr_debug("No local EDID present on DS branch device\n"); + pr_info("no downstream devices, skip client notification\n"); + goto end; + } + +read_edid: ret = mdss_dp_edid_read(dp); if (ret) { pr_err("edid read error, setting default resolution\n"); @@ -2056,14 +2185,18 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data, min(dp->max_pclk_khz, max_pclk_khz)); + if (dp->dpcd_read_required) { + pr_debug("reading DPCD with updated AUX config\n"); + mdss_dp_dpcd_cap_read(dp); + dp->dpcd_read_required = false; + } + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { pr_err("edid parse failed, setting default resolution\n"); goto notify; } - dp->sink_info_read = true; - notify: if (ret) { /* set failsafe parameters */ @@ -2090,7 +2223,6 @@ notify: end: pr_debug("end\n"); return ret; - } static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg) @@ -2291,12 +2423,16 @@ static ssize_t mdss_dp_rda_connected(struct device *dev, { ssize_t ret; struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) return -EINVAL; - ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->cable_connected); - pr_debug("%d\n", dp->cable_connected); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + ret = snprintf(buf, PAGE_SIZE, "%d\n", cable_connected); + pr_debug("%d\n", cable_connected); return ret; } @@ -2465,6 +2601,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, int hpd, rc; ssize_t ret = strnlen(buf, PAGE_SIZE); struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) { pr_err("invalid data\n"); @@ -2480,9 +2617,13 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, } dp->hpd = !!hpd; - pr_debug("hpd=%d\n", dp->hpd); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + pr_debug("hpd=%d cable_connected=%s\n", dp->hpd, + cable_connected ? "true" : "false"); - if (dp->hpd && dp->cable_connected) { + if (dp->hpd && cable_connected) { if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) { mdss_dp_host_init(&dp->panel_data); mdss_dp_process_hpd_high(dp); @@ -2812,8 +2953,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata) /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); - mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF); - reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, @@ -2903,28 +3042,33 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, switch (event) { case MDSS_EVENT_UNBLANK: - mdss_dp_ack_state(dp, true); rc = mdss_dp_on(pdata); break; case MDSS_EVENT_PANEL_ON: mdss_dp_update_hdcp_info(dp); if (dp_is_hdcp_enabled(dp)) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp_status = HDCP_STATE_AUTHENTICATING; queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ / 2); } break; + case MDSS_EVENT_POST_PANEL_ON: + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); + break; case MDSS_EVENT_PANEL_OFF: rc = mdss_dp_off(pdata); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); break; case MDSS_EVENT_BLANK: if (dp_is_hdcp_enabled(dp)) { dp->hdcp_status = HDCP_STATE_INACTIVE; - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); } @@ -3081,6 +3225,7 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev, static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp) { pr_debug("dp_video_ready\n"); + mdss_dp_ack_state(dp, true); complete(&dp->video_comp); } @@ -3112,10 +3257,21 @@ static int mdss_dp_event_thread(void *data) ev_data = (struct mdss_dp_event_data *)data; + pr_debug("starting\n"); while (!kthread_should_stop()) { wait_event(ev_data->event_q, (ev_data->pndx != ev_data->gndx) || - kthread_should_stop()); + kthread_should_stop() || + kthread_should_park()); + if (kthread_should_stop()) + return 0; + + if (kthread_should_park()) { + pr_debug("parking event thread\n"); + kthread_parkme(); + continue; + } + spin_lock_irqsave(&ev_data->event_lock, flag); ev = &(ev_data->event_list[ev_data->gndx++]); todo = ev->id; @@ -3207,6 +3363,7 @@ irqreturn_t dp_isr(int irq, void *ptr) spin_lock(&dp->lock); isr1 = dp_read(base + DP_INTR_STATUS); isr2 = dp_read(base + DP_INTR_STATUS2); + pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2); mask1 = isr1 & dp->mask1; @@ -3290,6 +3447,27 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) return 0; } +static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp) +{ + struct mdss_dp_event_data *ev_data = &dp->dp_event; + + spin_lock(&ev_data->event_lock); + ev_data->pndx = ev_data->gndx = 0; + spin_unlock(&ev_data->event_lock); + + mutex_lock(&dp->attention_lock); + INIT_LIST_HEAD(&dp->attention_head); + mutex_unlock(&dp->attention_lock); +} + +static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("enter\n"); + mdss_dp_reset_event_list(dp); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); +} + static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) { struct mdss_dp_drv_pdata *dp_drv; @@ -3301,6 +3479,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) } mdss_dp_update_cable_status(dp_drv, true); + if (dp_drv->ev_thread) + kthread_unpark(dp_drv->ev_thread); if (dp_drv->hpd) dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); @@ -3320,6 +3500,9 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) mdss_dp_update_cable_status(dp_drv, false); dp_drv->alt_mode.current_state = UNKNOWN_STATE; + mdss_dp_reset_sw_state(dp_drv); + kthread_park(dp_drv->ev_thread); + /** * Manually turn off the DP controller if we are in PHY * testing mode. @@ -3423,6 +3606,17 @@ static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp, if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD)) return; + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return; + } + } + mdss_dp_on_irq(dp, lt_needed); } @@ -3574,7 +3768,7 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) return -EINVAL; if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp.ops->off(dp->hdcp.data); } @@ -3620,10 +3814,46 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) static int mdss_dp_process_downstream_port_status_change( struct mdss_dp_drv_pdata *dp) { - if (!mdss_dp_is_downstream_port_status_changed(dp)) + bool ds_status_changed = false; + + if (mdss_dp_is_downstream_port_status_changed(dp)) { + pr_debug("downstream port status changed\n"); + ds_status_changed = true; + } + + /* + * Ideally sink should update the downstream port status changed + * whenever it updates the downstream sink count. However, it is + * possible that only the sink count is updated without setting + * the downstream port status changed bit. + */ + if (dp->sink_count.count != dp->prev_sink_count.count) { + pr_debug("downstream sink count changed from %d --> %d\n", + dp->prev_sink_count.count, dp->sink_count.count); + ds_status_changed = true; + } + + if (!ds_status_changed) return -EINVAL; - return mdss_dp_edid_read(dp); + mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD); + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return -ETIMEDOUT; + } + } + + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + pr_debug("sink count is zero, nothing to do\n"); + return 0; + } + + return mdss_dp_process_hpd_high(dp); } static bool mdss_dp_video_pattern_test_lt_needed(struct mdss_dp_drv_pdata *dp) @@ -3721,19 +3951,19 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) mdss_dp_aux_parse_sink_status_field(dp); - ret = mdss_dp_process_link_training_request(dp); + ret = mdss_dp_process_downstream_port_status_change(dp); if (!ret) goto exit; - ret = mdss_dp_process_phy_test_pattern_request(dp); + ret = mdss_dp_process_link_training_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_link_status_update(dp); + ret = mdss_dp_process_phy_test_pattern_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_downstream_port_status_change(dp); + ret = mdss_dp_process_link_status_update(dp); if (!ret) goto exit; @@ -3746,7 +3976,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) goto exit; pr_debug("done\n"); - exit: dp->hpd_irq_on = false; return ret; @@ -3792,7 +4021,8 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, node->vdo = *vdos; mutex_lock(&dp_drv->attention_lock); - list_add_tail(&node->list, &dp_drv->attention_head); + if (dp_drv->cable_connected) + list_add_tail(&node->list, &dp_drv->attention_head); mutex_unlock(&dp_drv->attention_lock); dp_send_events(dp_drv, EV_USBPD_ATTENTION); @@ -3840,11 +4070,21 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) if (!dp_drv->alt_mode.dp_status.hpd_high) { pr_debug("Attention: HPD low\n"); + if (!dp_drv->power_on) { + pr_debug("HPD already low\n"); + return; + } + if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->off) { - cancel_delayed_work(&dp_drv->hdcp_cb_work); + cancel_delayed_work_sync(&dp_drv->hdcp_cb_work); dp_drv->hdcp.ops->off(dp_drv->hdcp.data); } + /* + * Reset the sink count before nofifying clients since HPD Low + * indicates that the downstream device has been disconnected. + */ + mdss_dp_reset_sink_count(dp_drv); mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT); pr_debug("Attention: Notified clients\n"); @@ -3872,6 +4112,11 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) pr_debug("Attention: HPD high\n"); + if (dp_drv->power_on) { + pr_debug("HPD high processed already\n"); + return; + } + dp_drv->alt_mode.current_state |= DP_STATUS_DONE; if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) { @@ -3893,7 +4138,13 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) pr_debug("processing item %d in the list\n", ++i); + reinit_completion(&dp->notification_comp); mutex_lock(&dp->attention_lock); + if (!dp->cable_connected) { + pr_debug("cable disconnected, returning\n"); + mutex_unlock(&dp->attention_lock); + goto exit; + } node = list_first_entry(&dp->attention_head, struct mdss_dp_attention_node, list); @@ -3907,9 +4158,25 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status); mdss_dp_process_attention(dp); + if (atomic_read(&dp->notification_pending)) { + pr_debug("waiting for the attention event to finish\n"); + /* + * This wait is intentionally implemented without a + * timeout since this is happens only in possible error + * conditions e.g. if the display framework does not + * power off/on the DisplayPort device in time. Other + * events might already be queued from the sink at this + * point and they cannot be processed until the power + * off/on is complete otherwise we might have problems + * with interleaving of these events e.g. un-clocked + * register access. + */ + wait_for_completion(&dp->notification_comp); + } pr_debug("done processing item %d in the list\n", i); }; +exit: pr_debug("exit\n"); } @@ -3985,7 +4252,6 @@ 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->pd_msg_mutex); mutex_init(&dp_drv->attention_lock); mutex_init(&dp_drv->hdcp_mutex); spin_lock_init(&dp_drv->lock); @@ -4078,8 +4344,9 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->inited = true; dp_drv->hpd_irq_on = false; + atomic_set(&dp_drv->notification_pending, 0); mdss_dp_reset_test_data(dp_drv); - init_completion(&dp_drv->irq_comp); + init_completion(&dp_drv->notification_comp); dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN; pr_debug("done\n"); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 4decb26ea073..f358aad8a667 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -77,7 +77,7 @@ #define EDP_INTR_I2C_NACK BIT(18) #define EDP_INTR_I2C_DEFER BIT(21) #define EDP_INTR_PLL_UNLOCKED BIT(24) -#define EDP_INTR_AUX_ERROR BIT(27) +#define EDP_INTR_PHY_AUX_ERR BIT(27) #define EDP_INTR_STATUS1 \ @@ -85,7 +85,7 @@ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ - EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR) + EDP_INTR_PLL_UNLOCKED | EDP_INTR_PHY_AUX_ERR) #define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2) @@ -110,6 +110,8 @@ struct edp_buf { int len; /* dara length */ char trans_num; /* transaction number */ char i2c; /* 1 == i2c cmd, 0 == native cmd */ + bool no_send_addr; + bool no_send_stop; }; /* USBPD-TypeC specific Macros */ @@ -186,6 +188,7 @@ struct dp_alt_mode { #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) #define DPCD_NO_AUX_HANDSHAKE BIT(3) #define DPCD_PORT_0_EDID_PRESENTED BIT(4) +#define DPCD_PORT_1_EDID_PRESENTED BIT(5) /* event */ #define EV_EDP_AUX_SETUP BIT(0) @@ -239,6 +242,8 @@ struct downstream_port_config { bool oui_support; }; +#define DP_MAX_DS_PORT_COUNT 2 + struct dpcd_cap { char major; char minor; @@ -249,7 +254,7 @@ struct dpcd_cap { char enhanced_frame; u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */ u32 flags; - u32 rx_port0_buf_size; + u32 rx_port_buf_size[DP_MAX_DS_PORT_COUNT]; u32 training_read_interval;/* us */ struct downstream_port_config downstream_port; }; @@ -426,6 +431,102 @@ struct mdss_dp_crc_data { u32 b_cb; }; +#define MDSS_DP_MAX_PHY_CFG_VALUE_CNT 3 +struct mdss_dp_phy_cfg { + u32 cfg_cnt; + u32 current_index; + u32 offset; + u32 lut[MDSS_DP_MAX_PHY_CFG_VALUE_CNT]; +}; + +/* PHY AUX config registers */ +enum dp_phy_aux_config_type { + PHY_AUX_CFG0, + PHY_AUX_CFG1, + PHY_AUX_CFG2, + PHY_AUX_CFG3, + PHY_AUX_CFG4, + PHY_AUX_CFG5, + PHY_AUX_CFG6, + PHY_AUX_CFG7, + PHY_AUX_CFG8, + PHY_AUX_CFG9, + PHY_AUX_CFG_MAX, +}; + +static inline const char *mdss_dp_get_phy_aux_config_property(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return "qcom,aux-cfg0-settings"; + case PHY_AUX_CFG1: + return "qcom,aux-cfg1-settings"; + case PHY_AUX_CFG2: + return "qcom,aux-cfg2-settings"; + case PHY_AUX_CFG3: + return "qcom,aux-cfg3-settings"; + case PHY_AUX_CFG4: + return "qcom,aux-cfg4-settings"; + case PHY_AUX_CFG5: + return "qcom,aux-cfg5-settings"; + case PHY_AUX_CFG6: + return "qcom,aux-cfg6-settings"; + case PHY_AUX_CFG7: + return "qcom,aux-cfg7-settings"; + case PHY_AUX_CFG8: + return "qcom,aux-cfg8-settings"; + case PHY_AUX_CFG9: + return "qcom,aux-cfg9-settings"; + default: + return "unknown"; + } +} + +static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return DP_ENUM_STR(PHY_AUX_CFG0); + case PHY_AUX_CFG1: + return DP_ENUM_STR(PHY_AUX_CFG1); + case PHY_AUX_CFG2: + return DP_ENUM_STR(PHY_AUX_CFG2); + case PHY_AUX_CFG3: + return DP_ENUM_STR(PHY_AUX_CFG3); + case PHY_AUX_CFG4: + return DP_ENUM_STR(PHY_AUX_CFG4); + case PHY_AUX_CFG5: + return DP_ENUM_STR(PHY_AUX_CFG5); + case PHY_AUX_CFG6: + return DP_ENUM_STR(PHY_AUX_CFG6); + case PHY_AUX_CFG7: + return DP_ENUM_STR(PHY_AUX_CFG7); + case PHY_AUX_CFG8: + return DP_ENUM_STR(PHY_AUX_CFG8); + case PHY_AUX_CFG9: + return DP_ENUM_STR(PHY_AUX_CFG9); + default: + return "unknown"; + } +} + +enum dp_aux_transaction { + DP_AUX_WRITE, + DP_AUX_READ +}; + +static inline char *mdss_dp_aux_transaction_to_string(u32 transaction) +{ + switch (transaction) { + case DP_AUX_WRITE: + return DP_ENUM_STR(DP_AUX_WRITE); + case DP_AUX_READ: + return DP_ENUM_STR(DP_AUX_READ); + default: + return "unknown"; + } +} + struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); @@ -449,11 +550,11 @@ struct mdss_dp_drv_pdata { bool core_clks_on; bool link_clks_on; bool power_on; - bool sink_info_read; u32 suspend_vic; bool hpd; bool psm_enabled; bool audio_test_req; + bool dpcd_read_required; /* dp specific */ unsigned char *base; @@ -513,10 +614,9 @@ struct mdss_dp_drv_pdata { struct completion aux_comp; struct completion idle_comp; struct completion video_comp; - struct completion irq_comp; + struct completion notification_comp; struct mutex aux_mutex; struct mutex train_mutex; - struct mutex pd_msg_mutex; struct mutex attention_lock; struct mutex hdcp_mutex; bool cable_connected; @@ -540,13 +640,14 @@ struct mdss_dp_drv_pdata { struct dp_statistic dp_stat; bool hpd_irq_on; u32 hpd_notification_status; + atomic_t notification_pending; struct mdss_dp_event_data dp_event; struct task_struct *ev_thread; /* dt settings */ char l_map[4]; - u32 aux_cfg[AUX_CFG_LEN]; + struct mdss_dp_phy_cfg aux_cfg[PHY_AUX_CFG_MAX]; struct workqueue_struct *workq; struct delayed_work hdcp_cb_work; @@ -562,6 +663,7 @@ struct mdss_dp_drv_pdata { struct dpcd_test_request test_data; struct dpcd_sink_count sink_count; + struct dpcd_sink_count prev_sink_count; struct list_head attention_head; }; @@ -688,6 +790,7 @@ enum dp_aux_error { EDP_AUX_ERR_NACK = -3, EDP_AUX_ERR_DEFER = -4, EDP_AUX_ERR_NACK_DEFER = -5, + EDP_AUX_ERR_PHY = -6, }; static inline char *mdss_dp_get_aux_error(u32 aux_error) diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 8566b1d6985a..37209c161366 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -73,6 +73,21 @@ static int dp_buf_trailing(struct edp_buf *eb) return (int)(eb->end - eb->data); } +static void mdss_dp_aux_clear_hw_interrupts(void __iomem *phy_base) +{ + u32 data; + + data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS); + pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data); + + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0); + + /* Ensure that all interrupts are cleared and acked */ + wmb(); +} + /* * edp aux dp_buf_add_cmd: * NO native and i2c command mix allowed @@ -123,35 +138,46 @@ static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) return cmd->len - 1; } -static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) +static int dp_cmd_fifo_tx(struct mdss_dp_drv_pdata *dp) { u32 data; - char *dp; + char *datap; int len, cnt; + struct edp_buf *tp = &dp->txp; + void __iomem *base = dp->base; + len = tp->len; /* total byte to cmd fifo */ if (len == 0) return 0; cnt = 0; - dp = tp->start; + datap = tp->start; while (cnt < len) { - data = *dp; /* data byte */ + data = *datap; /* data byte */ data <<= 8; data &= 0x00ff00; /* index = 0, write */ if (cnt == 0) data |= BIT(31); /* INDEX_WRITE */ dp_write(base + DP_AUX_DATA, data); cnt++; - dp++; + datap++; } + /* clear the current tx request before queuing a new one */ + dp_write(base + DP_AUX_TRANS_CTRL, 0); + + /* clear any previous PHY AUX interrupts */ + mdss_dp_aux_clear_hw_interrupts(dp->phy_io.base); + data = (tp->trans_num - 1); if (tp->i2c) { data |= BIT(8); /* I2C */ - data |= BIT(10); /* NO SEND ADDR */ - data |= BIT(11); /* NO SEND STOP */ + if (tp->no_send_addr) + data |= BIT(10); /* NO SEND ADDR */ + if (tp->no_send_stop) + data |= BIT(11); /* NO SEND STOP */ } data |= BIT(9); /* GO */ @@ -164,7 +190,7 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) { u32 data; char *dp; - int i; + int i, actual_i; data = 0; /* index = 0 */ data |= BIT(31); /* INDEX_WRITE */ @@ -177,7 +203,12 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) data = dp_read(base + DP_AUX_DATA); for (i = 0; i < len; i++) { data = dp_read(base + DP_AUX_DATA); - *dp++ = (char)((data >> 8) & 0xff); + *dp++ = (char)((data >> 8) & 0xFF); + + actual_i = (data >> 16) & 0xFF; + if (i != actual_i) + pr_warn("Index mismatch: expected %d, found %d\n", + i, actual_i); } rp->len = len; @@ -214,9 +245,16 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - len = dp_cmd_fifo_tx(&ep->txp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = true; + len = dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux write timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + } if (ep->aux_error_num == EDP_AUX_ERR_NONE) ret = len; @@ -228,13 +266,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, return ret; } -int dp_aux_write(void *ep, struct edp_cmd *cmd) -{ - int rc = dp_aux_write_cmds(ep, cmd); - - return rc < 0 ? -EINVAL : 0; -} - static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { @@ -242,6 +273,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_buf *tp; struct edp_buf *rp; int len, ret; + u32 data; mutex_lock(&ep->aux_mutex); ep->aux_cmd_busy = 1; @@ -270,10 +302,23 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - dp_cmd_fifo_tx(tp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = false; + dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux read timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + ret = ep->aux_error_num; + goto end; + } + /* clear the current rx request before queuing a new one */ + data = dp_read(ep->base + DP_AUX_TRANS_CTRL); + data &= (~BIT(9)); + dp_write(ep->base + DP_AUX_TRANS_CTRL, data); if (ep->aux_error_num == EDP_AUX_ERR_NONE) { ret = dp_cmd_fifo_rx(rp, len, ep->base); @@ -284,58 +329,128 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, ret = ep->aux_error_num; } +end: ep->aux_cmd_busy = 0; mutex_unlock(&ep->aux_mutex); return ret; } -int dp_aux_read(void *ep, struct edp_cmd *cmds) -{ - int rc = dp_aux_read_cmds(ep, cmds); - - return rc < 0 ? -EINVAL : 0; -} - void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - if (isr & EDP_INTR_AUX_I2C_DONE) + pr_debug("isr=0x%08x\n", isr); + if (isr & EDP_INTR_AUX_I2C_DONE) { ep->aux_error_num = EDP_AUX_ERR_NONE; - else if (isr & EDP_INTR_WRONG_ADDR) + } else if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } complete(&ep->aux_comp); } void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { + pr_debug("isr=0x%08x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) { if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)) ep->aux_error_num = EDP_AUX_ERR_NACK; else ep->aux_error_num = EDP_AUX_ERR_NONE; } else { - if (isr & EDP_INTR_WRONG_ADDR) + if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK_DEFER; - if (isr & EDP_INTR_I2C_NACK) + } else if (isr & EDP_INTR_I2C_NACK) { ep->aux_error_num = EDP_AUX_ERR_NACK; - if (isr & EDP_INTR_I2C_DEFER) + } else if (isr & EDP_INTR_I2C_DEFER) { ep->aux_error_num = EDP_AUX_ERR_DEFER; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } } complete(&ep->aux_comp); } -static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - char *buf, int len, int i2c) +static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp, + struct edp_cmd *cmd, enum dp_aux_transaction transaction) +{ + int const retry_count = 5; + int adjust_count = 0; + int i; + u32 aux_cfg1_config_count; + int ret; + + aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp, + PHY_AUX_CFG1); +retry: + i = 0; + ret = 0; + do { + struct edp_cmd cmd1 = *cmd; + + dp->aux_error_num = EDP_AUX_ERR_NONE; + pr_debug("Trying %s, iteration count: %d\n", + mdss_dp_aux_transaction_to_string(transaction), + i + 1); + if (transaction == DP_AUX_READ) + ret = dp_aux_read_cmds(dp, &cmd1); + else if (transaction == DP_AUX_WRITE) + ret = dp_aux_write_cmds(dp, &cmd1); + + i++; + } while ((i < retry_count) && (ret < 0)); + + if (ret >= 0) /* rw success */ + goto end; + + if (adjust_count >= aux_cfg1_config_count) { + pr_err("PHY_AUX_CONFIG1 calibration failed\n"); + goto end; + } + + /* Adjust AUX configuration and retry */ + pr_debug("AUX failure (%d), adjust AUX settings\n", ret); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + adjust_count++; + goto retry; + +end: + return ret; +} + +/** + * dp_aux_write_buf_retry() - send a AUX write command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c, bool retry) { struct edp_cmd cmd; @@ -346,11 +461,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_write_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE); + else + return dp_aux_write_cmds(dp, &cmd); } -static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - int len, int i2c) +static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c) +{ + return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true); +} + +int dp_aux_write(void *dp, struct edp_cmd *cmd) +{ + int rc = dp_aux_write_cmds(dp, cmd); + + return rc < 0 ? -EINVAL : 0; +} + +/** + * dp_aux_read_buf_retry() - send a AUX read command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c, bool retry) { struct edp_cmd cmd = {0}; @@ -361,7 +507,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_read_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ); + else + return dp_aux_read_cmds(dp, &cmd); +} + +static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c) +{ + return dp_aux_read_buf_retry(dp, addr, len, i2c, true); +} + +int dp_aux_read(void *dp, struct edp_cmd *cmds) +{ + int rc = dp_aux_read_cmds(dp, cmds); + + return rc < 0 ? -EINVAL : 0; } /* @@ -733,16 +895,68 @@ static void dp_aux_send_checksum(struct mdss_dp_drv_pdata *dp, u32 checksum) dp_aux_write_buf(dp, 0x260, data, 1, 0); } +int mdss_dp_aux_read_edid(struct mdss_dp_drv_pdata *dp, + u8 *buf, int size, int blk_num) +{ + int max_size_bytes = 16; + int rc, read_size; + int ret = 0; + u8 offset_lut[] = {0x0, 0x80}; + u8 offset; + + if (dp->test_data.test_requested == TEST_EDID_READ) + max_size_bytes = 128; + + /* + * Calculate the offset of the desired EDID block to be read. + * For even blocks, offset starts at 0x0 + * For odd blocks, offset starts at 0x80 + */ + if (blk_num % 2) + offset = offset_lut[1]; + else + offset = offset_lut[0]; + + do { + struct edp_cmd cmd = {0}; + + read_size = min(size, max_size_bytes); + cmd.read = 1; + cmd.addr = EDID_START_ADDRESS; + cmd.len = read_size; + cmd.out_buf = buf; + cmd.i2c = 1; + + /* Write the offset first prior to reading the data */ + pr_debug("offset=0x%x, size=%d\n", offset, size); + dp_aux_write_buf_retry(dp, EDID_START_ADDRESS, &offset, 1, 1, + false); + rc = dp_aux_read(dp, &cmd); + if (rc < 0) { + pr_err("aux read failed\n"); + return rc; + } + + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + buf, read_size, false); + buf += read_size; + offset += read_size; + size -= read_size; + ret += read_size; + } while (size > 0); + + return ret; +} + int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) { - struct edp_buf *rp = &dp->rxp; int rlen, ret = 0; int edid_blk = 0, blk_num = 0, retries = 10; bool edid_parsing_done = false; - const u8 cea_tag = 0x02, start_ext_blk = 0x1; u32 const segment_addr = 0x30; u32 checksum = 0; - char segment = 0x1; + bool phy_aux_update_requested = false; + bool ext_block_parsing_done = false; ret = dp_aux_chan_ready(dp); if (ret) { @@ -750,72 +964,91 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) return ret; } + memset(dp->edid_buf, 0, dp->edid_buf_size); + /** * Parse the test request vector to see whether there is a * TEST_EDID_READ test request. */ dp_sink_parse_test_request(dp); - do { - rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS + - (blk_num * EDID_BLOCK_SIZE), - EDID_BLOCK_SIZE, 1); + while (retries) { + u8 segment; + u8 edid_buf[EDID_BLOCK_SIZE] = {0}; + + /* + * Write the segment first. + * Segment = 0, for blocks 0 and 1 + * Segment = 1, for blocks 2 and 3 + * Segment = 2, for blocks 3 and 4 + * and so on ... + */ + segment = blk_num >> 1; + dp_aux_write_buf_retry(dp, segment_addr, &segment, 1, 1, false); + + rlen = mdss_dp_aux_read_edid(dp, edid_buf, EDID_BLOCK_SIZE, + blk_num); if (rlen != EDID_BLOCK_SIZE) { - pr_err("Read failed. rlen=%d\n", rlen); + pr_err("Read failed. rlen=%s\n", + mdss_dp_get_aux_error(rlen)); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } - pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen); - - if (dp_edid_is_valid_header(rp->data)) { - ret = dp_edid_buf_error(rp->data, rp->len); + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + edid_buf, EDID_BLOCK_SIZE, false); + if (dp_edid_is_valid_header(edid_buf)) { + ret = dp_edid_buf_error(edid_buf, rlen); if (ret) { pr_err("corrupt edid block detected\n"); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } if (edid_parsing_done) { + pr_debug("block 0 parsed already\n"); blk_num++; + retries--; continue; } - dp_extract_edid_manufacturer(&dp->edid, rp->data); - dp_extract_edid_product(&dp->edid, rp->data); - dp_extract_edid_version(&dp->edid, rp->data); - dp_extract_edid_ext_block_cnt(&dp->edid, rp->data); - dp_extract_edid_video_support(&dp->edid, rp->data); - dp_extract_edid_feature(&dp->edid, rp->data); + dp_extract_edid_manufacturer(&dp->edid, edid_buf); + dp_extract_edid_product(&dp->edid, edid_buf); + dp_extract_edid_version(&dp->edid, edid_buf); + dp_extract_edid_ext_block_cnt(&dp->edid, edid_buf); + dp_extract_edid_video_support(&dp->edid, edid_buf); + dp_extract_edid_feature(&dp->edid, edid_buf); dp_extract_edid_detailed_timing_description(&dp->edid, - rp->data); + edid_buf); edid_parsing_done = true; + } else if (!edid_parsing_done) { + pr_debug("Invalid edid block 0 header\n"); + /* Retry block 0 with adjusted phy aux settings */ + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; + continue; } else { edid_blk++; blk_num++; - - /* fix dongle byte shift issue */ - if (edid_blk == 1 && rp->data[0] != cea_tag) { - u8 tmp[EDID_BLOCK_SIZE - 1]; - - memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1); - rp->data[0] = cea_tag; - memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1); - } } memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE), - rp->data, EDID_BLOCK_SIZE); + edid_buf, EDID_BLOCK_SIZE); - checksum = rp->data[rp->len - 1]; + checksum = edid_buf[rlen - 1]; /* break if no more extension blocks present */ - if (edid_blk == dp->edid.ext_block_cnt) + if (edid_blk >= dp->edid.ext_block_cnt) { + ext_block_parsing_done = true; break; - - /* write segment number to read block 3 onwards */ - if (edid_blk == start_ext_blk) - dp_aux_write_buf(dp, segment_addr, &segment, 1, 1); - } while (retries--); + } + } if (dp->test_data.test_requested == TEST_EDID_READ) { pr_debug("sending checksum %d\n", checksum); @@ -823,6 +1056,18 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) dp->test_data = (const struct dpcd_test_request){ 0 }; } + /* + * Trigger the reading of DPCD if there was a change in the AUX + * configuration caused by a failure while reading the EDID. + * This is required to ensure the integrity and validity + * of the sink capabilities read that will subsequently be used + * to establish the mainlink. + */ + if (edid_parsing_done && ext_block_parsing_done + && phy_aux_update_requested) { + dp->dpcd_read_required = true; + } + return ret; } @@ -834,6 +1079,10 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) struct dpcd_cap *cap; struct edp_buf *rp; int rlen; + int i; + + cap = &ep->dpcd; + memset(cap, 0, sizeof(*cap)); rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { @@ -848,11 +1097,8 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) } rp = &ep->rxp; - cap = &ep->dpcd; bp = rp->data; - memset(cap, 0, sizeof(*cap)); - data = *bp++; /* byte 0 */ cap->major = (data >> 4) & 0x0f; cap->minor = data & 0x0f; @@ -909,6 +1155,11 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ cap->downstream_port.dfp_count = data & 0x7; + if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) { + pr_debug("DS port count %d greater that max (%d) supported\n", + cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT); + cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT; + } cap->downstream_port.msa_timing_par_ignored = data & BIT(6); cap->downstream_port.oui_support = data & BIT(7); pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n", @@ -916,17 +1167,23 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) cap->downstream_port.msa_timing_par_ignored); pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); - data = *bp++; /* byte 8 */ - if (data & BIT(1)) { - cap->flags |= DPCD_PORT_0_EDID_PRESENTED; - pr_debug("edid presented\n"); - } - - data = *bp++; /* byte 9 */ - cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size); + for (i = 0; i < DP_MAX_DS_PORT_COUNT; i++) { + data = *bp++; /* byte 8 + i*2 */ + pr_debug("parsing capabilities for DS port %d\n", i); + if (data & BIT(1)) { + if (i == 0) + cap->flags |= DPCD_PORT_0_EDID_PRESENTED; + else + cap->flags |= DPCD_PORT_1_EDID_PRESENTED; + pr_debug("local edid present\n"); + } else { + pr_debug("local edid absent\n"); + } - bp += 2; /* skip 10, 11 port1 capability */ + data = *bp++; /* byte 9 + i*2 */ + cap->rx_port_buf_size[i] = (data + 1) * 32; + pr_debug("lane_buf_size=%d\n", cap->rx_port_buf_size[i]); + } data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; @@ -1258,6 +1515,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) int const param_len = 0x1; int const sink_count_addr = 0x200; + ep->prev_sink_count = ep->sink_count; + rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0); if (rlen < param_len) { pr_err("failed to read sink count\n"); @@ -2363,8 +2622,8 @@ clear: void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) { dp_sink_parse_sink_count(ep); - dp_sink_parse_test_request(ep); mdss_dp_aux_link_status_read(ep, 6); + dp_sink_parse_test_request(ep); } int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index ea492f54054c..0d9cf7b72b4d 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -858,6 +858,48 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate, pr_debug("dp_tu=0x%x\n", dp_tu); } +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io) +{ + u32 const max_aux_timeout_count = 0xFFFFF; + u32 const max_aux_limits = 0xFFFFFFFF; + + pr_debug("timeout=0x%x, limits=0x%x\n", + max_aux_timeout_count, max_aux_limits); + + writel_relaxed(max_aux_timeout_count, + ctrl_io->base + DP_AUX_TIMEOUT_COUNT); + writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS); +} + +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type) +{ + u32 new_index; + struct dss_io_data *phy_io = &dp->phy_io; + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, + config_type); + + if (!cfg) { + pr_err("invalid config type %s", + mdss_dp_phy_aux_config_type_to_string(config_type)); + return; + } + + new_index = (cfg->current_index + 1) % cfg->cfg_cnt; + + pr_debug("Updating %s from 0x%08x to 0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(config_type), + cfg->lut[cfg->current_index], cfg->lut[new_index]); + writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset); + cfg->current_index = new_index; + + /* Make sure the new HW configuration takes effect */ + wmb(); + + /* Reset the AUX controller before any subsequent transactions */ + mdss_dp_aux_reset(&dp->ctrl_io); +} + void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) { u8 bits_per_lane = 2; @@ -870,26 +912,24 @@ void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); } -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset) +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp) { - void __iomem *adjusted_phy_io_base = phy_io->base + phy_reg_offset; + int i; + void __iomem *adjusted_phy_io_base = dp->phy_io.base + + dp->phy_reg_offset; writel_relaxed(0x3d, adjusted_phy_io_base + DP_PHY_PD_CTL); - /* DP AUX CFG register programming */ - writel_relaxed(aux_cfg[0], adjusted_phy_io_base + DP_PHY_AUX_CFG0); - writel_relaxed(aux_cfg[1], adjusted_phy_io_base + DP_PHY_AUX_CFG1); - writel_relaxed(aux_cfg[2], adjusted_phy_io_base + DP_PHY_AUX_CFG2); - writel_relaxed(aux_cfg[3], adjusted_phy_io_base + DP_PHY_AUX_CFG3); - writel_relaxed(aux_cfg[4], adjusted_phy_io_base + DP_PHY_AUX_CFG4); - writel_relaxed(aux_cfg[5], adjusted_phy_io_base + DP_PHY_AUX_CFG5); - writel_relaxed(aux_cfg[6], adjusted_phy_io_base + DP_PHY_AUX_CFG6); - writel_relaxed(aux_cfg[7], adjusted_phy_io_base + DP_PHY_AUX_CFG7); - writel_relaxed(aux_cfg[8], adjusted_phy_io_base + DP_PHY_AUX_CFG8); - writel_relaxed(aux_cfg[9], adjusted_phy_io_base + DP_PHY_AUX_CFG9); - - writel_relaxed(0x1f, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, i); + + pr_debug("%s: offset=0x%08x, value=0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(i), cfg->offset, + cfg->lut[cfg->current_index]); + writel_relaxed(cfg->lut[cfg->current_index], + dp->phy_io.base + cfg->offset); + }; + writel_relaxed(0x1e, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); } int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 8f19e7cdf3cf..4c93e48e97dc 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -35,6 +35,8 @@ #define DP_AUX_CTRL (0x00000230) #define DP_AUX_DATA (0x00000234) #define DP_AUX_TRANS_CTRL (0x00000238) +#define DP_AUX_TIMEOUT_COUNT (0x0000023C) +#define DP_AUX_LIMITS (0x00000240) #define DP_AUX_STATUS (0x00000244) #define DP_DPCD_CP_IRQ (0x201) @@ -163,6 +165,7 @@ #define DP_PHY_AUX_CFG9 (0x00000040) #define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) #define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) +#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000B8) #define DP_PHY_SPARE0 0x00A8 @@ -271,6 +274,19 @@ static const struct dp_vc_tu_mapping_table tu_table[] = { 0x21, 0x000c, false, 0x00, 0x00, 0x00, 0x27}, }; +static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return &dp->aux_cfg[cfg_type]; +} + +static inline u32 mdss_dp_phy_aux_get_config_cnt( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return dp->aux_cfg[cfg_type].cfg_cnt; +} + +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io); int dp_aux_read(void *ep, struct edp_cmd *cmds); int dp_aux_write(void *ep, struct edp_cmd *cmd); void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); @@ -285,8 +301,9 @@ 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, u8 link_rate, u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo); void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc); -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset); +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp); +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type); void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable); diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 17722eac3006..b1552829508d 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -2713,10 +2713,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_reconfig(pdata, mode); break; case MDSS_EVENT_DSI_PANEL_STATUS: - if (ctrl_pdata->check_status) - rc = ctrl_pdata->check_status(ctrl_pdata); - else - rc = true; + rc = mdss_dsi_check_panel_status(ctrl_pdata, arg); break; case MDSS_EVENT_PANEL_TIMING_SWITCH: rc = mdss_dsi_panel_timing_switch(ctrl_pdata, arg); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 2a76466abf3e..00f23380591b 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -704,6 +704,7 @@ void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, u32 mask, u32 val); int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl); +int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg); static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 37f3929a3a2c..3b48bb642792 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -3031,7 +3031,10 @@ bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl) * warning message is ignored. */ if (ctrl->panel_data.panel_info.esd_check_enabled && - (ctrl->status_mode == ESD_BTA) && (status & 0x1008000)) + ((ctrl->status_mode == ESD_BTA) || + (ctrl->status_mode == ESD_REG) || + (ctrl->status_mode == ESD_REG_NT35596)) && + (status & 0x1008000)) return false; pr_err("%s: status=%x\n", __func__, status); @@ -3262,8 +3265,10 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) * cleared. */ if (ctrl->panel_data.panel_info.esd_check_enabled && - (ctrl->status_mode == ESD_BTA) && - (ctrl->panel_mode == DSI_VIDEO_MODE)) { + ((ctrl->status_mode == ESD_BTA) || + (ctrl->status_mode == ESD_REG) || + (ctrl->status_mode == ESD_REG_NT35596)) && + (ctrl->panel_mode == DSI_VIDEO_MODE)) { isr &= ~DSI_INTR_ERROR; /* clear only overflow */ mdss_dsi_set_reg(ctrl, 0x0c, 0x44440000, 0x44440000); diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 4208c2c43efb..0f24f66dbcc6 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -39,6 +39,35 @@ static uint32_t interval = STATUS_CHECK_INTERVAL_MS; static int32_t dsi_status_disable = DSI_STATUS_CHECK_INIT; struct dsi_status_data *pstatus_data; +int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg) +{ + struct mdss_mdp_ctl *ctl = NULL; + struct msm_fb_data_type *mfd = arg; + int ret = 0; + + if (!mfd) + return -EINVAL; + + ctl = mfd_to_ctl(mfd); + + if (!ctl || !ctrl) + return -EINVAL; + + mutex_lock(&ctl->offlock); + /* + * if check_status method is not defined + * then no need to fail this function, + * instead return a positive value. + */ + if (ctrl->check_status) + ret = ctrl->check_status(ctrl); + else + ret = 1; + mutex_unlock(&ctl->offlock); + + return ret; +} + /* * check_dsi_ctrl_status() - Reads MFD structure and * calls platform specific DSI ctrl Status function. diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 93643246935e..698c5633cf6a 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -653,7 +653,7 @@ static ssize_t mdss_fb_get_panel_status(struct device *dev, ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", "suspend"); } else { panel_status = mdss_fb_send_panel_event(mfd, - MDSS_EVENT_DSI_PANEL_STATUS, NULL); + MDSS_EVENT_DSI_PANEL_STATUS, mfd); ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", panel_status > 0 ? "alive" : "dead"); } @@ -1596,13 +1596,30 @@ static int mdss_fb_resume(struct platform_device *pdev) static int mdss_fb_pm_suspend(struct device *dev) { struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int rc = 0; if (!mfd) return -ENODEV; dev_dbg(dev, "display pm suspend\n"); - return mdss_fb_suspend_sub(mfd); + rc = mdss_fb_suspend_sub(mfd); + + /* + * Call MDSS footswitch control to ensure GDSC is + * off after pm suspend call. There are cases when + * mdss runtime call doesn't trigger even when clock + * ref count is zero after fb pm suspend. + */ + if (!rc) { + if (mfd->mdp.footswitch_ctrl) + mfd->mdp.footswitch_ctrl(false); + } else { + pr_err("fb pm suspend failed, rc: %d\n", rc); + } + + return rc; + } static int mdss_fb_pm_resume(struct device *dev) @@ -1622,6 +1639,9 @@ static int mdss_fb_pm_resume(struct device *dev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + if (mfd->mdp.footswitch_ctrl) + mfd->mdp.footswitch_ctrl(true); + return mdss_fb_resume_sub(mfd); } #endif diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 8e5fc5949770..518c24810acd 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -232,6 +232,7 @@ struct msm_mdp_interface { int (*configure_panel)(struct msm_fb_data_type *mfd, int mode, int dest_ctrl); int (*input_event_handler)(struct msm_fb_data_type *mfd); + void (*footswitch_ctrl)(bool on); int (*pp_release_fnc)(struct msm_fb_data_type *mfd); void *private1; }; diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 834726e84bda..2dc9c8f96c5b 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -1381,7 +1381,8 @@ int hdcp_1x_authenticate(void *input) flush_delayed_work(&hdcp->hdcp_auth_work); - if (!hdcp_1x_state(HDCP_STATE_INACTIVE)) { + if (!hdcp_1x_state(HDCP_STATE_INACTIVE) && + !hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) { pr_err("invalid state\n"); return -EINVAL; } @@ -1443,7 +1444,6 @@ int hdcp_1x_reauthenticate(void *input) DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); - hdcp->hdcp_state = HDCP_STATE_INACTIVE; hdcp_1x_authenticate(hdcp); return ret; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 37c4be6135aa..599f6cb44c63 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -2604,6 +2604,11 @@ void hdmi_edid_set_video_resolution(void *input, u32 resolution, bool reset) return; } + if (resolution == HDMI_VFRMT_UNKNOWN) { + pr_debug("%s: Default video resolution not set\n", __func__); + return; + } + edid_ctrl->video_resolution = resolution; if (reset) { diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 171f44815430..a645a3495593 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -233,7 +233,6 @@ static struct mdss_mdp_irq mdp_irq_map[] = { static struct intr_callback *mdp_intr_cb; -static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on); static int mdss_mdp_parse_dt(struct platform_device *pdev); static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev); static int mdss_mdp_parse_dt_mixer(struct platform_device *pdev); @@ -5172,7 +5171,7 @@ static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata) * active (but likely in an idle state), the vote for the CX and the batfet * rails should not be released. */ -static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) +void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) { int ret; int active_cnt = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 56af021e8cfc..db037ed263b4 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1640,6 +1640,7 @@ int mdss_mdp_set_intr_callback_nosync(u32 intr_type, u32 intf_num, void (*fnc_ptr)(void *), void *arg); u32 mdss_mdp_get_irq_mask(u32 intr_type, u32 intf_num); +void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on); void mdss_mdp_footswitch_ctrl_splash(int on); void mdss_mdp_batfet_ctrl(struct mdss_data_type *mdata, int enable); void mdss_mdp_set_clk_rate(unsigned long min_clk_rate, bool locked); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index c800bbe4963c..8c612e2b83fb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -6291,6 +6291,13 @@ int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd) return rc; } +void mdss_mdp_footswitch_ctrl_handler(bool on) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + mdss_mdp_footswitch_ctrl(mdata, on); +} + int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; @@ -6333,6 +6340,14 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) mdp5_interface->configure_panel = mdss_mdp_update_panel_info; mdp5_interface->input_event_handler = mdss_mdp_input_event_handler; + /* + * Register footswitch control only for primary fb pm + * suspend/resume calls. + */ + if (mfd->panel_info->is_prim_panel) + mdp5_interface->footswitch_ctrl = + mdss_mdp_footswitch_ctrl_handler; + if (mfd->panel_info->type == WRITEBACK_PANEL) { mdp5_interface->atomic_validate = mdss_mdp_layer_atomic_validate_wfd; diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h index 029e11f9919b..6a56f0039f15 100644 --- a/include/linux/io-pgtable-fast.h +++ b/include/linux/io-pgtable-fast.h @@ -37,7 +37,7 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size); #define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base, - u64 end, bool skip_sync); + u64 start, u64 end, bool skip_sync); void av8l_register_notify(struct notifier_block *nb); #else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */ @@ -46,6 +46,7 @@ void av8l_register_notify(struct notifier_block *nb); static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base, + u64 start, u64 end, bool skip_sync) { diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 654bb97a3188..5bc4836af286 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -77,6 +77,12 @@ struct pmu_hw_events { struct arm_pmu *percpu_pmu; }; +enum armpmu_pmu_states { + ARM_PMU_STATE_OFF, + ARM_PMU_STATE_RUNNING, + ARM_PMU_STATE_GOING_DOWN, +}; + struct arm_pmu { struct pmu pmu; cpumask_t active_irqs; @@ -101,6 +107,8 @@ struct arm_pmu { void (*free_irq)(struct arm_pmu *); int (*map_event)(struct perf_event *event); int num_events; + int pmu_state; + int percpu_irq; atomic_t active_events; struct mutex reserve_mutex; u64 max_period; diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h index 7fca674b6230..b025df568259 100644 --- a/include/linux/qpnp/qpnp-revid.h +++ b/include/linux/qpnp/qpnp-revid.h @@ -214,6 +214,11 @@ #define PM660L_V1P1_REV3 0x01 #define PM660L_V1P1_REV4 0x01 +#define PM660L_V2P0_REV1 0x00 +#define PM660L_V2P0_REV2 0x00 +#define PM660L_V2P0_REV3 0x00 +#define PM660L_V2P0_REV4 0x02 + /* PMI8998 FAB_ID */ #define PMI8998_FAB_ID_SMIC 0x11 #define PMI8998_FAB_ID_GF 0x30 diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index e10a04c9cdc7..cf336d670f8b 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -135,6 +135,18 @@ config IP6_NF_IPTABLES if IP6_NF_IPTABLES +config IP6_NF_IPTABLES_128 + tristate "128 bit arithmetic for iptables matching" + depends on IP6_NF_IPTABLES + help + This enables 128 bit matching in ip6tables to help optimize cases + where there is no match required. ip6tables matching for ipv6 always + has a mask if an address is specified for match. Adding a check for + mask prior to that helps to improve performance as it avoids the + masked comparison. + + Note that this feature depends on the architecture. If unsure, say N. + # The simple matches. config IP6_NF_MATCH_AH tristate '"ah" match support' diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 22f39e00bef3..6fd784643d6e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -94,22 +94,26 @@ ip6_packet_match(const struct sk_buff *skb, { unsigned long ret; const struct ipv6hdr *ipv6 = ipv6_hdr(skb); +#if IS_ENABLED(IP6_NF_IPTABLES_128) + const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk; + const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk; +#endif #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg))) - if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk, - &ip6info->src), IP6T_INV_SRCIP) || - FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk, - &ip6info->dst), IP6T_INV_DSTIP)) { - dprintf("Source or dest mismatch.\n"); -/* - dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, - ipinfo->smsk.s_addr, ipinfo->src.s_addr, - ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : ""); - dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, - ipinfo->dmsk.s_addr, ipinfo->dst.s_addr, - ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/ - return false; +#if IS_ENABLED(IP6_NF_IPTABLES_128) + if (*ulm1 || *ulm2) +#endif + { + if (FWINV(ipv6_masked_addr_cmp + (&ipv6->saddr, &ip6info->smsk, &ip6info->src), + IP6T_INV_SRCIP) || + FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk, + &ip6info->dst), + IP6T_INV_DSTIP)) { + dprintf("Source or dest mismatch.\n"); + return false; + } } ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask); |
