Merge "spi: spi-geni-qcom: Fix the chip select setup parameters" into msm-4.9
diff --git a/Documentation/arm64/tagged-pointers.txt b/Documentation/arm64/tagged-pointers.txt
index d9995f1..a25a99e 100644
--- a/Documentation/arm64/tagged-pointers.txt
+++ b/Documentation/arm64/tagged-pointers.txt
@@ -11,24 +11,56 @@
The kernel configures the translation tables so that translations made
via TTBR0 (i.e. userspace mappings) have the top byte (bits 63:56) of
the virtual address ignored by the translation hardware. This frees up
-this byte for application use, with the following caveats:
+this byte for application use.
- (1) The kernel requires that all user addresses passed to EL1
- are tagged with tag 0x00. This means that any syscall
- parameters containing user virtual addresses *must* have
- their top byte cleared before trapping to the kernel.
- (2) Non-zero tags are not preserved when delivering signals.
- This means that signal handlers in applications making use
- of tags cannot rely on the tag information for user virtual
- addresses being maintained for fields inside siginfo_t.
- One exception to this rule is for signals raised in response
- to watchpoint debug exceptions, where the tag information
- will be preserved.
+Passing tagged addresses to the kernel
+--------------------------------------
- (3) Special care should be taken when using tagged pointers,
- since it is likely that C compilers will not hazard two
- virtual addresses differing only in the upper byte.
+All interpretation of userspace memory addresses by the kernel assumes
+an address tag of 0x00.
+
+This includes, but is not limited to, addresses found in:
+
+ - pointer arguments to system calls, including pointers in structures
+ passed to system calls,
+
+ - the stack pointer (sp), e.g. when interpreting it to deliver a
+ signal,
+
+ - the frame pointer (x29) and frame records, e.g. when interpreting
+ them to generate a backtrace or call graph.
+
+Using non-zero address tags in any of these locations may result in an
+error code being returned, a (fatal) signal being raised, or other modes
+of failure.
+
+For these reasons, passing non-zero address tags to the kernel via
+system calls is forbidden, and using a non-zero address tag for sp is
+strongly discouraged.
+
+Programs maintaining a frame pointer and frame records that use non-zero
+address tags may suffer impaired or inaccurate debug and profiling
+visibility.
+
+
+Preserving tags
+---------------
+
+Non-zero tags are not preserved when delivering signals. This means that
+signal handlers in applications making use of tags cannot rely on the
+tag information for user virtual addresses being maintained for fields
+inside siginfo_t. One exception to this rule is for signals raised in
+response to watchpoint debug exceptions, where the tag information will
+be preserved.
The architecture prevents the use of a tagged PC, so the upper byte will
be set to a sign-extension of bit 55 on exception return.
+
+
+Other considerations
+--------------------
+
+Special care should be taken when using tagged pointers, since it is
+likely that C compilers will not hazard two virtual addresses differing
+only in the upper byte.
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_memory_dump.txt b/Documentation/devicetree/bindings/arm/msm/msm_memory_dump.txt
new file mode 100644
index 0000000..a415c8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm_memory_dump.txt
@@ -0,0 +1,31 @@
+Qualcomm Technologies Inc. memory dump driver
+
+QTI memory dump driver allows various client subsystems to register and allocate respective
+dump regions. At the time of deadlocks or cpu hangs these dump regions
+are captured to give a snapshot of the system at the time of the crash.
+
+Required properties:
+
+-compatible: "qcom,mem-dump"
+-memory-region: phandle to the CMA region. The size of the CMA region
+ should be greater than sum of size of all child nodes
+ to account for padding.
+
+If any child nodes exist the following property are required:
+
+-qcom,dump-size: The size of memory that needs to be allocated for the
+ particular node.
+-qcom,dump-id: The ID within the data dump table where this entry needs
+ to be added.
+
+Example:
+
+ mem_dump {
+ compatible = "qcom,mem-dump";
+ memory-region = <&dump_mem>;
+
+ rpmh_dump {
+ qcom,dump-size = <0x2000000>;
+ qcom,dump-id = <0xEC>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index c766df8..b0eed20 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -297,6 +297,10 @@
- qcom,sde-downscaling-prefill-lines: A u32 value indicates the latency of downscaling in lines.
- qcom,sde-max-per-pipe-bw-kbps: Array of u32 value indicates the max per pipe bandwidth in Kbps.
- qcom,sde-amortizable-threshold: This value indicates the min for traffic shaping in lines.
+- qcom,sde-vbif-qos-rt-remap: This array is used to program vbif qos remapper register
+ priority for realtime clients.
+- qcom,sde-vbif-qos-nrt-remap: This array is used to program vbif qos remapper register
+ priority for non-realtime clients.
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
@@ -500,6 +504,9 @@
2400000 2400000 2400000 2400000>;
qcom,sde-amortizable-threshold = <11>;
+ qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>;
+ qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>;
+
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x320>;
qcom,sde-vig-qseed-off = <0x200>;
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
new file mode 100644
index 0000000..8598d0c
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
@@ -0,0 +1,63 @@
+QTI PDC interrupt controller
+
+PDC is QTI's platform parent interrupt controller that serves as wakeup source.
+
+Newer QTI SOCs are replacing MPM (MSM sleep Power Manager) with PDC (Power
+Domain Controller) to manage subsystem wakeups and resources during sleep.
+This driver marks the wakeup interrupts in APSS PDC such that it monitors the
+interrupts when the system is asleep, wakes up the APSS when one of these
+interrupts occur and replays it to the subsystem interrupt controller after it
+becomes operational.
+
+Earlier MPM architecture used arch-extension of GIC interrupt
+controller to mark enabled wake-up interrupts and monitor these when the
+system goes to sleep. Since the arch-extensions are no-longer available
+on newer kernel versions, this driver is implemented as hierarchical irq
+domain. GIC is parent interrupt controller at the highest level.
+Platform interrupt controller PDC is next in hierarchy, followed by others.
+This driver only configures the interrupts, does not handle them.
+
+PDC interrupt configuration involves programming of 2 set of registers:
+IRQ_ENABLE_BANK - Enable the irq
+IRQ_i_CFG - Configure the interrupt i
+
+Properties:
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,pdc-<target>"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Specifies the base physical address for PDC hardware
+ block for DRV2.
+
+- interrupt-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Specifies the number of cells needed to encode an interrupt source.
+ Value must be 3.
+ The encoding of these cells are same as described in
+ Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
+
+- interrupt-parent:
+ Usage: required
+ Value type: <phandle>
+ Definition: Specifies the interrupt parent necessary for hierarchical domain to operate.
+
+- interrupt-controller:
+ Usage: required
+ Value type: <bool>
+ Definition: Identifies the node as an interrupt controller.
+
+Example:
+
+pdcgic: interrupt-controller@0xb220000{
+ compatible = "qcom,pdc-sdm845";
+ reg = <0xb220000 0x30000>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
+};
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 2d971b7a..375eaf2 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -86,6 +86,11 @@
useful if the upstream hardware is capable of switching
between multiple domains within a single context bank.
+- qcom,use-3-lvl-tables:
+ Some hardware configurations may not be optimized for using
+ a four level page table configuration. Set to use a three
+ level page table instead.
+
- clocks : List of clocks to be used during SMMU register access. See
Documentation/devicetree/bindings/clock/clock-bindings.txt
for information about the format. For each clock specified
diff --git a/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt b/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
index 0295e1b..937ccb9 100644
--- a/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
@@ -81,6 +81,32 @@
limits.
- qcom,mdss-rot-vbif-qos-setting: This array is used to program vbif qos remapper register
priority for rotator clients.
+- qcom,mdss-rot-cdp-setting: Integer array of size two, to indicate client driven
+ prefetch is available or not. Index 0 represents
+ if CDP is enabled for read and index 1, if CDP
+ is enabled for write operation.
+- qcom,mdss-rot-qos-lut A 4 cell property with the format of <rd_lut_0,
+ rd_lut_1, wr_lut_0, wr_lut_1> indicating the qos
+ lut settings for the rotator sspp and writeback
+ client.
+- qcom,mdss-rot-danger-lut A two cell property with the format of <rd_lut,
+ wr_lut> indicating the danger lut settings for
+ the rotator sspp and writeback client.
+- qcom,mdss-rot-safe-lut A two cell property with the format of <rd_lut,
+ wr_lut> indicating the safe lut settings for the
+ rotator sspp and writeback client.
+- qcom,mdss-inline-rot-qos-lut: A 4 cell property with the format of <rd_lut_0,
+ rd_lut_1, wr_lut_0, wr_lut_1> indicating the qos
+ lut settings for the inline rotator sspp and
+ writeback client.
+- qcom,mdss-inline-rot-danger-lut: A two cell property with the format of
+ <rd_lut, wr_lut> indicating the danger lut
+ settings for the inline rotator sspp and
+ writeback client.
+- qcom,mdss-inline-rot-safe-lut: A two cell property with the format of
+ <rd_lut, wr_lut> indicating the safe lut
+ settings for the inline rotator sspp and
+ writeback client.
- qcom,mdss-rot-mode: This is integer value indicates operation mode
of the rotator device
- qcom,mdss-sbuf-headroom: This integer value indicates stream buffer headroom in lines.
@@ -146,9 +172,19 @@
/* VBIF QoS remapper settings*/
qcom,mdss-rot-vbif-qos-setting = <1 1 1 1>;
+ com,mdss-rot-cdp-setting = <1 1>;
+
qcom,mdss-default-ot-rd-limit = <8>;
qcom,mdss-default-ot-wr-limit = <16>;
+ qcom,mdss-rot-qos-lut = <0x0 0x0 0x0 0x0>;
+ qcom,mdss-rot-danger-lut = <0x0 0x0>;
+ qcom,mdss-rot-safe-lut = <0x0000ffff 0x0>;
+
+ qcom,mdss-inline-rot-qos-lut = <0x0 0x0 0x00112233 0x44556677>;
+ qcom,mdss-inline-rot-danger-lut = <0x0 0x0000ffff>;
+ qcom,mdss-inline-rot-safe-lut = <0x0 0x0000ff00>;
+
qcom,mdss-sbuf-headroom = <20>;
cache-slice-names = "rotator";
cache-slices = <&llcc 4>;
diff --git a/Documentation/devicetree/bindings/nfc/nq-nci.txt b/Documentation/devicetree/bindings/nfc/nq-nci.txt
new file mode 100644
index 0000000..b85e070
--- /dev/null
+++ b/Documentation/devicetree/bindings/nfc/nq-nci.txt
@@ -0,0 +1,49 @@
+Qualcomm Technologies, Inc NQxxxx NFC NCI device
+
+Near Field Communication (NFC) device is based on NFC Controller Interface (NCI)
+
+Required properties:
+
+- compatible: "qcom,nq-nci"
+- reg: NCI i2c slave address.
+- qcom,nq-ven: specific gpio for hardware reset.
+- qcom,nq-irq: specific gpio for read interrupt.
+- qcom,nq-firm: gpio for firmware download
+- qcom,nq-clkreq: gpio for clock
+- interrupt-parent: Should be phandle for the interrupt controller
+ that services interrupts for this device.
+- interrupts: Nfc read interrupt,gpio-clk-req interrupt
+
+
+Recommended properties:
+
+- interrupt-names: names of interrupts, should include "nfc_irq", used for reference
+
+
+Optional properties:
+
+- pinctrl-names, pinctrl-0, pincntrl-1: references to our pincntrl settings
+- clocks, clock-names: must contain the NQxxxx's core clock.
+- qcom,nq-esepwr: gpio to control power of secure element
+
+Example:
+
+ nq-nci@2b {
+ compatible = "qcom,nq-nci";
+ reg = <0x2b>;
+ qcom,nq-irq = <&tlmm 29 0x00>;
+ qcom,nq-ven = <&tlmm 30 0x00>;
+ qcom,nq-firm = <&tlmm 93 0x00>;
+ qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ qcom,clk-src = "BBCLK2";
+ interrupt-parent = <&tlmm>;
+ interrupts = <29 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active","nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_disable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+ qcom,clk-gpio = <&pm8916_gpios 2 0>;
+ clocks = <&clock_rpm clk_bb_clk2_pin>;
+ clock-names = "ref_clk";
+ };
diff --git a/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt b/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt
index d7edafc..4a69e03 100644
--- a/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt
+++ b/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt
@@ -67,6 +67,7 @@
- qcom,complete-ramdump: Boolean. If set, complete ramdump i.e. region between start address of
first segment to end address of last segment will be collected without
leaving any hole in between.
+- qcom,ignore-ssr-failure: Boolean. If set, SSR failures are not considered fatal.
Example:
qcom,venus@fdce0000 {
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index 53b872c..db86cda 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -308,6 +308,12 @@
.. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
:export:
+Explicit Fencing Properties
+---------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
+ :doc: explicit fencing properties
+
Existing KMS Properties
-----------------------
diff --git a/Makefile b/Makefile
index b9aba93..df4d437 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 29
+SUBLEVEL = 30
EXTRAVERSION =
NAME = Roaring Lionus
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index ffb93f49..4f95577 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1188,8 +1188,10 @@
if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur)))
return -EFAULT;
- err = 0;
- err |= put_user(status, ustatus);
+ err = put_user(status, ustatus);
+ if (ret < 0)
+ return err ? err : ret;
+
err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec);
err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec);
err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec);
diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
index c51fc65..5a53fcf 100644
--- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
@@ -162,9 +162,10 @@
};
adc0: adc@f8018000 {
+ atmel,adc-vref = <3300>;
+ atmel,adc-channels-used = <0xfe>;
pinctrl-0 = <
&pinctrl_adc0_adtrg
- &pinctrl_adc0_ad0
&pinctrl_adc0_ad1
&pinctrl_adc0_ad2
&pinctrl_adc0_ad3
@@ -172,8 +173,6 @@
&pinctrl_adc0_ad5
&pinctrl_adc0_ad6
&pinctrl_adc0_ad7
- &pinctrl_adc0_ad8
- &pinctrl_adc0_ad9
>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6sx-sdb.dts b/arch/arm/boot/dts/imx6sx-sdb.dts
index 5bb8fd5..d71da30 100644
--- a/arch/arm/boot/dts/imx6sx-sdb.dts
+++ b/arch/arm/boot/dts/imx6sx-sdb.dts
@@ -12,23 +12,6 @@
model = "Freescale i.MX6 SoloX SDB RevB Board";
};
-&cpu0 {
- operating-points = <
- /* kHz uV */
- 996000 1250000
- 792000 1175000
- 396000 1175000
- 198000 1175000
- >;
- fsl,soc-operating-points = <
- /* ARM kHz SOC uV */
- 996000 1250000
- 792000 1175000
- 396000 1175000
- 198000 1175000
- >;
-};
-
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index 4e361a8..b4bfa55 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -569,6 +569,7 @@
regulator-name = "+3VS,vdd_pnl";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
gpio = <&gpio TEGRA_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
enable-active-high;
};
diff --git a/arch/arm/include/asm/kvm_coproc.h b/arch/arm/include/asm/kvm_coproc.h
index 4917c2f..e74ab0f 100644
--- a/arch/arm/include/asm/kvm_coproc.h
+++ b/arch/arm/include/asm/kvm_coproc.h
@@ -31,7 +31,8 @@
int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run);
-int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
index 464748b..ed23196 100644
--- a/arch/arm/include/asm/module.h
+++ b/arch/arm/include/asm/module.h
@@ -18,13 +18,18 @@
};
#endif
+struct mod_plt_sec {
+ struct elf32_shdr *plt;
+ int plt_count;
+};
+
struct mod_arch_specific {
#ifdef CONFIG_ARM_UNWIND
struct unwind_table *unwind[ARM_SEC_MAX];
#endif
#ifdef CONFIG_ARM_MODULE_PLTS
- struct elf32_shdr *plt;
- int plt_count;
+ struct mod_plt_sec core;
+ struct mod_plt_sec init;
#endif
};
diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c
index 3a5cba9..3d0c2e4 100644
--- a/arch/arm/kernel/module-plts.c
+++ b/arch/arm/kernel/module-plts.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -31,9 +31,17 @@
u32 lit[PLT_ENT_COUNT];
};
+static bool in_init(const struct module *mod, unsigned long loc)
+{
+ return loc - (u32)mod->init_layout.base < mod->init_layout.size;
+}
+
u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
{
- struct plt_entries *plt = (struct plt_entries *)mod->arch.plt->sh_addr;
+ struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
+ &mod->arch.init;
+
+ struct plt_entries *plt = (struct plt_entries *)pltsec->plt->sh_addr;
int idx = 0;
/*
@@ -41,9 +49,9 @@
* relocations are sorted, this will be the last entry we allocated.
* (if one exists).
*/
- if (mod->arch.plt_count > 0) {
- plt += (mod->arch.plt_count - 1) / PLT_ENT_COUNT;
- idx = (mod->arch.plt_count - 1) % PLT_ENT_COUNT;
+ if (pltsec->plt_count > 0) {
+ plt += (pltsec->plt_count - 1) / PLT_ENT_COUNT;
+ idx = (pltsec->plt_count - 1) % PLT_ENT_COUNT;
if (plt->lit[idx] == val)
return (u32)&plt->ldr[idx];
@@ -53,8 +61,8 @@
plt++;
}
- mod->arch.plt_count++;
- BUG_ON(mod->arch.plt_count * PLT_ENT_SIZE > mod->arch.plt->sh_size);
+ pltsec->plt_count++;
+ BUG_ON(pltsec->plt_count * PLT_ENT_SIZE > pltsec->plt->sh_size);
if (!idx)
/* Populate a new set of entries */
@@ -129,7 +137,7 @@
/* Count how many PLT entries we may need */
static unsigned int count_plts(const Elf32_Sym *syms, Elf32_Addr base,
- const Elf32_Rel *rel, int num)
+ const Elf32_Rel *rel, int num, Elf32_Word dstidx)
{
unsigned int ret = 0;
const Elf32_Sym *s;
@@ -144,13 +152,17 @@
case R_ARM_THM_JUMP24:
/*
* We only have to consider branch targets that resolve
- * to undefined symbols. This is not simply a heuristic,
- * it is a fundamental limitation, since the PLT itself
- * is part of the module, and needs to be within range
- * as well, so modules can never grow beyond that limit.
+ * to symbols that are defined in a different section.
+ * This is not simply a heuristic, it is a fundamental
+ * limitation, since there is no guaranteed way to emit
+ * PLT entries sufficiently close to the branch if the
+ * section size exceeds the range of a branch
+ * instruction. So ignore relocations against defined
+ * symbols if they live in the same section as the
+ * relocation target.
*/
s = syms + ELF32_R_SYM(rel[i].r_info);
- if (s->st_shndx != SHN_UNDEF)
+ if (s->st_shndx == dstidx)
break;
/*
@@ -161,7 +173,12 @@
* So we need to support them, but there is no need to
* take them into consideration when trying to optimize
* this code. So let's only check for duplicates when
- * the addend is zero.
+ * the addend is zero. (Note that calls into the core
+ * module via init PLT entries could involve section
+ * relative symbol references with non-zero addends, for
+ * which we may end up emitting duplicates, but the init
+ * PLT is released along with the rest of the .init
+ * region as soon as module loading completes.)
*/
if (!is_zero_addend_relocation(base, rel + i) ||
!duplicate_rel(base, rel, i))
@@ -174,7 +191,8 @@
int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod)
{
- unsigned long plts = 0;
+ unsigned long core_plts = 0;
+ unsigned long init_plts = 0;
Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum;
Elf32_Sym *syms = NULL;
@@ -184,13 +202,15 @@
*/
for (s = sechdrs; s < sechdrs_end; ++s) {
if (strcmp(".plt", secstrings + s->sh_name) == 0)
- mod->arch.plt = s;
+ mod->arch.core.plt = s;
+ else if (strcmp(".init.plt", secstrings + s->sh_name) == 0)
+ mod->arch.init.plt = s;
else if (s->sh_type == SHT_SYMTAB)
syms = (Elf32_Sym *)s->sh_addr;
}
- if (!mod->arch.plt) {
- pr_err("%s: module PLT section missing\n", mod->name);
+ if (!mod->arch.core.plt || !mod->arch.init.plt) {
+ pr_err("%s: module PLT section(s) missing\n", mod->name);
return -ENOEXEC;
}
if (!syms) {
@@ -213,16 +233,29 @@
/* sort by type and symbol index */
sort(rels, numrels, sizeof(Elf32_Rel), cmp_rel, NULL);
- plts += count_plts(syms, dstsec->sh_addr, rels, numrels);
+ if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0)
+ core_plts += count_plts(syms, dstsec->sh_addr, rels,
+ numrels, s->sh_info);
+ else
+ init_plts += count_plts(syms, dstsec->sh_addr, rels,
+ numrels, s->sh_info);
}
- mod->arch.plt->sh_type = SHT_NOBITS;
- mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
- mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
- mod->arch.plt->sh_size = round_up(plts * PLT_ENT_SIZE,
- sizeof(struct plt_entries));
- mod->arch.plt_count = 0;
+ mod->arch.core.plt->sh_type = SHT_NOBITS;
+ mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+ mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES;
+ mod->arch.core.plt->sh_size = round_up(core_plts * PLT_ENT_SIZE,
+ sizeof(struct plt_entries));
+ mod->arch.core.plt_count = 0;
- pr_debug("%s: plt=%x\n", __func__, mod->arch.plt->sh_size);
+ mod->arch.init.plt->sh_type = SHT_NOBITS;
+ mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+ mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES;
+ mod->arch.init.plt->sh_size = round_up(init_plts * PLT_ENT_SIZE,
+ sizeof(struct plt_entries));
+ mod->arch.init.plt_count = 0;
+
+ pr_debug("%s: plt=%x, init.plt=%x\n", __func__,
+ mod->arch.core.plt->sh_size, mod->arch.init.plt->sh_size);
return 0;
}
diff --git a/arch/arm/kernel/module.lds b/arch/arm/kernel/module.lds
index 05881e2..eacb5c6 100644
--- a/arch/arm/kernel/module.lds
+++ b/arch/arm/kernel/module.lds
@@ -1,3 +1,4 @@
SECTIONS {
.plt : { BYTE(0) }
+ .init.plt : { BYTE(0) }
}
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 3e5e419..c3ed6bd 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -93,12 +93,6 @@
return 1;
}
-int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run)
-{
- kvm_inject_undefined(vcpu);
- return 1;
-}
-
static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
{
/*
@@ -514,12 +508,7 @@
return 1;
}
-/**
- * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access
- * @vcpu: The VCPU pointer
- * @run: The kvm_run struct
- */
-int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static struct coproc_params decode_64bit_hsr(struct kvm_vcpu *vcpu)
{
struct coproc_params params;
@@ -533,9 +522,38 @@
params.Rt2 = (kvm_vcpu_get_hsr(vcpu) >> 10) & 0xf;
params.CRm = 0;
+ return params;
+}
+
+/**
+ * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access
+ * @vcpu: The VCPU pointer
+ * @run: The kvm_run struct
+ */
+int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ struct coproc_params params = decode_64bit_hsr(vcpu);
+
return emulate_cp15(vcpu, ¶ms);
}
+/**
+ * kvm_handle_cp14_64 -- handles a mrrc/mcrr trap on a guest CP14 access
+ * @vcpu: The VCPU pointer
+ * @run: The kvm_run struct
+ */
+int kvm_handle_cp14_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ struct coproc_params params = decode_64bit_hsr(vcpu);
+
+ /* raz_wi cp14 */
+ pm_fake(vcpu, ¶ms, NULL);
+
+ /* handled */
+ kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
+ return 1;
+}
+
static void reset_coproc_regs(struct kvm_vcpu *vcpu,
const struct coproc_reg *table, size_t num)
{
@@ -546,12 +564,7 @@
table[i].reset(vcpu, &table[i]);
}
-/**
- * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access
- * @vcpu: The VCPU pointer
- * @run: The kvm_run struct
- */
-int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+static struct coproc_params decode_32bit_hsr(struct kvm_vcpu *vcpu)
{
struct coproc_params params;
@@ -565,9 +578,37 @@
params.Op2 = (kvm_vcpu_get_hsr(vcpu) >> 17) & 0x7;
params.Rt2 = 0;
+ return params;
+}
+
+/**
+ * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access
+ * @vcpu: The VCPU pointer
+ * @run: The kvm_run struct
+ */
+int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ struct coproc_params params = decode_32bit_hsr(vcpu);
return emulate_cp15(vcpu, ¶ms);
}
+/**
+ * kvm_handle_cp14_32 -- handles a mrc/mcr trap on a guest CP14 access
+ * @vcpu: The VCPU pointer
+ * @run: The kvm_run struct
+ */
+int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ struct coproc_params params = decode_32bit_hsr(vcpu);
+
+ /* raz_wi cp14 */
+ pm_fake(vcpu, ¶ms, NULL);
+
+ /* handled */
+ kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
+ return 1;
+}
+
/******************************************************************************
* Userspace API
*****************************************************************************/
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c
index 4e40d19..066b6d4 100644
--- a/arch/arm/kvm/handle_exit.c
+++ b/arch/arm/kvm/handle_exit.c
@@ -83,9 +83,9 @@
[HSR_EC_WFI] = kvm_handle_wfx,
[HSR_EC_CP15_32] = kvm_handle_cp15_32,
[HSR_EC_CP15_64] = kvm_handle_cp15_64,
- [HSR_EC_CP14_MR] = kvm_handle_cp14_access,
+ [HSR_EC_CP14_MR] = kvm_handle_cp14_32,
[HSR_EC_CP14_LS] = kvm_handle_cp14_load_store,
- [HSR_EC_CP14_64] = kvm_handle_cp14_access,
+ [HSR_EC_CP14_64] = kvm_handle_cp14_64,
[HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access,
[HSR_EC_CP10_ID] = kvm_handle_cp10_id,
[HSR_EC_HVC] = handle_hvc,
diff --git a/arch/arm/kvm/hyp/Makefile b/arch/arm/kvm/hyp/Makefile
index 3023bb5..8679405 100644
--- a/arch/arm/kvm/hyp/Makefile
+++ b/arch/arm/kvm/hyp/Makefile
@@ -2,6 +2,8 @@
# Makefile for Kernel-based Virtual Machine module, HYP part
#
+ccflags-y += -fno-stack-protector
+
KVM=../../../../virt/kvm
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
diff --git a/arch/arm/kvm/hyp/switch.c b/arch/arm/kvm/hyp/switch.c
index 92678b7..624a510 100644
--- a/arch/arm/kvm/hyp/switch.c
+++ b/arch/arm/kvm/hyp/switch.c
@@ -48,7 +48,9 @@
write_sysreg(HSTR_T(15), HSTR);
write_sysreg(HCPTR_TTA | HCPTR_TCP(10) | HCPTR_TCP(11), HCPTR);
val = read_sysreg(HDCR);
- write_sysreg(val | HDCR_TPM | HDCR_TPMCR, HDCR);
+ val |= HDCR_TPM | HDCR_TPMCR; /* trap performance monitors */
+ val |= HDCR_TDRA | HDCR_TDOSA | HDCR_TDA; /* trap debug regs */
+ write_sysreg(val, HDCR);
}
static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S
index 8dea616..5049777 100644
--- a/arch/arm/mm/proc-v7m.S
+++ b/arch/arm/mm/proc-v7m.S
@@ -147,10 +147,10 @@
@ Configure caches (if implemented)
teq r8, #0
- stmneia r12, {r0-r6, lr} @ v7m_invalidate_l1 touches r0-r6
+ stmneia sp, {r0-r6, lr} @ v7m_invalidate_l1 touches r0-r6
blne v7m_invalidate_l1
teq r8, #0 @ re-evalutae condition
- ldmneia r12, {r0-r6, lr}
+ ldmneia sp, {r0-r6, lr}
@ Configure the System Control Register to ensure 8-byte stack alignment
@ Note the STKALIGN bit is either RW or RAO.
diff --git a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
index 17839db..509a2ed 100644
--- a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi
@@ -772,6 +772,7 @@
clocks = <&sys_ctrl 2>, <&sys_ctrl 1>;
clock-names = "ciu", "biu";
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC0>;
+ reset-names = "reset";
bus-width = <0x8>;
vmmc-supply = <&ldo19>;
pinctrl-names = "default";
@@ -795,6 +796,7 @@
clocks = <&sys_ctrl 4>, <&sys_ctrl 3>;
clock-names = "ciu", "biu";
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC1>;
+ reset-names = "reset";
vqmmc-supply = <&ldo7>;
vmmc-supply = <&ldo10>;
bus-width = <0x4>;
@@ -813,6 +815,7 @@
clocks = <&sys_ctrl HI6220_MMC2_CIUCLK>, <&sys_ctrl HI6220_MMC2_CLK>;
clock-names = "ciu", "biu";
resets = <&sys_ctrl PERIPH_RSTDIS0_MMC2>;
+ reset-names = "reset";
bus-width = <0x4>;
broken-cd;
pinctrl-names = "default", "idle";
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index 6a3e8b4..ea89751 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -20,6 +20,7 @@
reg = <0x5040000 0x10000>;
#iommu-cells = <1>;
qcom,dynamic;
+ qcom,use-3-lvl-tables;
#global-interrupts = <2>;
qcom,regulator-names = "vdd";
vdd-supply = <&gpu_cx_gdsc>;
@@ -63,6 +64,7 @@
reg-names = "base", "tcu-base";
#iommu-cells = <2>;
qcom,skip-init;
+ qcom,use-3-lvl-tables;
#global-interrupts = <1>;
#size-cells = <1>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
index 122299c..f27b9da 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
@@ -22,7 +22,7 @@
qcom,board-id = <1 1>;
};
-&dsi_dual_nt35597_truly_video_display {
+&dsi_dual_nt35597_truly_cmd_display {
/delete-property/ qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
index 55e615c..4627e60 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
@@ -22,7 +22,7 @@
qcom,board-id = <8 1>;
};
-&dsi_dual_nt35597_truly_video_display {
+&dsi_dual_nt35597_truly_cmd_display {
/delete-property/ qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
index 1702e80..b1c91bf 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
@@ -1073,6 +1073,15 @@
qcom,bcms = <&bcm_sn4>;
};
+ mas_alc: mas-alc {
+ cell-id = <MSM_BUS_MASTER_ALC>;
+ label = "mas-alc";
+ qcom,buswidth = <1>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_mc_virt>;
+ qcom,bcms = <&bcm_alc>;
+ };
+
mas_llcc_mc_display: mas-llcc-mc_display {
cell-id = <MSM_BUS_MASTER_LLCC_DISPLAY>;
label = "mas-llcc-mc_display";
@@ -1635,7 +1644,7 @@
qcom,buswidth = <4>;
qcom,agg-ports = <4>;
qcom,bus-dev = <&fab_mc_virt>;
- qcom,bcms = <&bcm_mc0>;
+ qcom,bcms = <&bcm_mc0>, <&bcm_acv>;
};
slv_qhs_mdsp_ms_mpu_cfg:slv-qhs-mdsp-ms-mpu-cfg {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 4c642e3..cb20e0f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -464,7 +464,7 @@
cell-index = <0>;
label = "cam-cdm-intf";
num-hw-cdm = <1>;
- cdm-client-names = "ife",
+ cdm-client-names = "vfe",
"jpeg-dma",
"jpeg",
"fd";
@@ -493,7 +493,7 @@
<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
clock-rates = <0 0 0 0 0>;
- cdm-client-names = "vfe";
+ cdm-client-names = "ife";
status = "ok";
};
@@ -761,24 +761,18 @@
"cpas_ahb_clk",
"camnoc_axi_clk",
"icp_apb_clk",
- "icp_atb_clk",
"icp_clk",
- "icp_clk_src",
- "icp_cti_clk",
- "icp_ts_clk";
+ "icp_clk_src";
clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
<&clock_gcc GCC_CAMERA_AXI_CLK>,
<&clock_camcc CAM_CC_SOC_AHB_CLK>,
<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
<&clock_camcc CAM_CC_ICP_APB_CLK>,
- <&clock_camcc CAM_CC_ICP_ATB_CLK>,
<&clock_camcc CAM_CC_ICP_CLK>,
- <&clock_camcc CAM_CC_ICP_CLK_SRC>,
- <&clock_camcc CAM_CC_ICP_CTI_CLK>,
- <&clock_camcc CAM_CC_ICP_TS_CLK>;
+ <&clock_camcc CAM_CC_ICP_CLK_SRC>;
- clock-rates = <0 0 0 80000000 0 0 0 0 600000000 0 0>;
+ clock-rates = <0 0 0 80000000 0 0 0 600000000>;
fw_name = "CAMERA_ICP.elf";
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 1fdf740..7038d48 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -291,6 +291,24 @@
&qupv3_se3_i2c {
status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 63 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <63 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_enable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 1b3f2a6..92f8586 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -65,7 +65,7 @@
qcom,gpu-quirk-hfi-use-reg;
- qcom,idle-timeout = <100000000>; //msecs
+ qcom,idle-timeout = <80>; //msecs
qcom,no-nap;
qcom,highest-bank-bit = <15>;
@@ -237,7 +237,7 @@
label = "kgsl-gmu";
compatible = "qcom,gpu-gmu";
- reg = <0x506a000 0x26000>, <0xb200000 0x300000>;
+ reg = <0x506a000 0x30000>, <0xb200000 0x300000>;
reg-names = "kgsl_gmu_reg", "kgsl_gmu_pdc_reg";
interrupts = <0 304 0>, <0 305 0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 508b645..521fd6b3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -262,6 +262,24 @@
&qupv3_se3_i2c {
status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 63 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <63 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_enable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index da5d6fa..c7a4d7d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -39,44 +39,44 @@
36 37>;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0xffffffff>;
- interrupt-map = <0 0 0 0 &intc 0 141 0
- 0 0 0 1 &intc 0 149 0
- 0 0 0 2 &intc 0 150 0
- 0 0 0 3 &intc 0 151 0
- 0 0 0 4 &intc 0 152 0
- 0 0 0 5 &intc 0 140 0
- 0 0 0 6 &intc 0 672 0
- 0 0 0 7 &intc 0 673 0
- 0 0 0 8 &intc 0 674 0
- 0 0 0 9 &intc 0 675 0
- 0 0 0 10 &intc 0 676 0
- 0 0 0 11 &intc 0 677 0
- 0 0 0 12 &intc 0 678 0
- 0 0 0 13 &intc 0 679 0
- 0 0 0 14 &intc 0 680 0
- 0 0 0 15 &intc 0 681 0
- 0 0 0 16 &intc 0 682 0
- 0 0 0 17 &intc 0 683 0
- 0 0 0 18 &intc 0 684 0
- 0 0 0 19 &intc 0 685 0
- 0 0 0 20 &intc 0 686 0
- 0 0 0 21 &intc 0 687 0
- 0 0 0 22 &intc 0 688 0
- 0 0 0 23 &intc 0 689 0
- 0 0 0 24 &intc 0 690 0
- 0 0 0 25 &intc 0 691 0
- 0 0 0 26 &intc 0 692 0
- 0 0 0 27 &intc 0 693 0
- 0 0 0 28 &intc 0 694 0
- 0 0 0 29 &intc 0 695 0
- 0 0 0 30 &intc 0 696 0
- 0 0 0 31 &intc 0 697 0
- 0 0 0 32 &intc 0 698 0
- 0 0 0 33 &intc 0 699 0
- 0 0 0 34 &intc 0 700 0
- 0 0 0 35 &intc 0 701 0
- 0 0 0 36 &intc 0 702 0
- 0 0 0 37 &intc 0 703 0>;
+ interrupt-map = <0 0 0 0 &pdc 0 141 0
+ 0 0 0 1 &pdc 0 149 0
+ 0 0 0 2 &pdc 0 150 0
+ 0 0 0 3 &pdc 0 151 0
+ 0 0 0 4 &pdc 0 152 0
+ 0 0 0 5 &pdc 0 140 0
+ 0 0 0 6 &pdc 0 672 0
+ 0 0 0 7 &pdc 0 673 0
+ 0 0 0 8 &pdc 0 674 0
+ 0 0 0 9 &pdc 0 675 0
+ 0 0 0 10 &pdc 0 676 0
+ 0 0 0 11 &pdc 0 677 0
+ 0 0 0 12 &pdc 0 678 0
+ 0 0 0 13 &pdc 0 679 0
+ 0 0 0 14 &pdc 0 680 0
+ 0 0 0 15 &pdc 0 681 0
+ 0 0 0 16 &pdc 0 682 0
+ 0 0 0 17 &pdc 0 683 0
+ 0 0 0 18 &pdc 0 684 0
+ 0 0 0 19 &pdc 0 685 0
+ 0 0 0 20 &pdc 0 686 0
+ 0 0 0 21 &pdc 0 687 0
+ 0 0 0 22 &pdc 0 688 0
+ 0 0 0 23 &pdc 0 689 0
+ 0 0 0 24 &pdc 0 690 0
+ 0 0 0 25 &pdc 0 691 0
+ 0 0 0 26 &pdc 0 692 0
+ 0 0 0 27 &pdc 0 693 0
+ 0 0 0 28 &pdc 0 694 0
+ 0 0 0 29 &pdc 0 695 0
+ 0 0 0 30 &pdc 0 696 0
+ 0 0 0 31 &pdc 0 697 0
+ 0 0 0 32 &pdc 0 698 0
+ 0 0 0 33 &pdc 0 699 0
+ 0 0 0 34 &pdc 0 700 0
+ 0 0 0 35 &pdc 0 701 0
+ 0 0 0 36 &pdc 0 702 0
+ 0 0 0 37 &pdc 0 703 0>;
interrupt-names = "int_msi", "int_a", "int_b", "int_c",
"int_d", "int_global_int",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 1744574..f534891 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -19,6 +19,7 @@
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
+ interrupt-parent = <&pdc>;
ufs_dev_reset_assert: ufs_dev_reset_assert {
config {
@@ -1555,6 +1556,68 @@
};
};
+ nfc {
+ nfc_int_active: nfc_int_active {
+ /* active state */
+ mux {
+ /* GPIO 63 NFC Read Interrupt */
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_int_suspend: nfc_int_suspend {
+ /* sleep state */
+ mux {
+ /* GPIO 63 NFC Read Interrupt */
+ pins = "gpio63";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio63";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_active: nfc_enable_active {
+ /* active state */
+ mux {
+ /* 12: NFC ENABLE 116:ESE Enable */
+ pins = "gpio12", "gpio62", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio62", "gpio116";
+ drive-strength = <2>; /* 2 MA */
+ bias-pull-up;
+ };
+ };
+
+ nfc_enable_suspend: nfc_enable_suspend {
+ /* sleep state */
+ mux {
+ /* 12: NFC ENABLE 116:ESE Enable */
+ pins = "gpio12", "gpio62", "gpio116";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio12", "gpio62", "gpio116";
+ drive-strength = <2>; /* 2 MA */
+ bias-disable;
+ };
+ };
+ };
+
qupv3_se3_spi_pins: qupv3_se3_spi_pins {
qupv3_se3_spi_active: qupv3_se3_spi_active {
mux {
@@ -2680,6 +2743,14 @@
};
&pm8998_gpios {
+ gpio@d400 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+
key_home {
key_home_default: key_home_default {
pins = "gpio5";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index 70e749b..6806145 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -145,4 +145,12 @@
reg = <0xC300000 0x1000>, <0xC3F0004 0x4>;
reg-names = "phys_addr_base", "offset_addr";
};
+
+ pdc: interrupt-controller@0xb220000{
+ compatible = "qcom,pdc-sdm845";
+ reg = <0xb220000 0x400>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index b51996d..3bf1ea4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -14,6 +14,27 @@
#include <dt-bindings/gpio/gpio.h>
/{
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-io-supply = <&pm8998_s3>;
+ qca,bt-vdd-xtal-supply = <&pm8998_s5>;
+ qca,bt-vdd-core-supply = <&pm8998_l7>;
+ qca,bt-vdd-pa-supply = <&pm8998_l17>;
+ qca,bt-vdd-ldo-supply = <&pm8998_l25>;
+
+ qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+ qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+ qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+
+ qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+ };
+
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
#include "fg-gen3-batterydata-itech-3000mah.dtsi"
@@ -39,6 +60,24 @@
&qupv3_se3_i2c {
status = "ok";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 63 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 62 0x00>;
+ qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <63 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_enable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+ clock-names = "ref_clk";
+ };
};
&qupv3_se10_i2c {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
index 0fb455f..1fa6e26 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
@@ -42,7 +42,7 @@
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se6_4uart_active>;
pinctrl-1 = <&qupv3_se6_4uart_sleep>;
- interrupts-extended = <&intc GIC_SPI 607 0>,
+ interrupts-extended = <&pdc GIC_SPI 607 0>,
<&tlmm 48 0>;
status = "disabled";
qcom,wakeup-byte = <0xFD>;
@@ -60,7 +60,7 @@
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se7_4uart_active>;
pinctrl-1 = <&qupv3_se7_4uart_sleep>;
- interrupts-extended = <&intc GIC_SPI 608 0>,
+ interrupts-extended = <&pdc GIC_SPI 608 0>,
<&tlmm 96 0>;
status = "disabled";
qcom,wakeup-byte = <0xFD>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 7ae63af..19b8744 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -36,7 +36,7 @@
reg = <0x3500 0x100>;
regulator-name = "pm8998_s12";
regulator-min-microvolt = <568000>;
- regulator-max-microvolt = <1056000>;
+ regulator-max-microvolt = <1136000>;
qcom,enable-time = <500>;
regulator-always-on;
};
@@ -114,9 +114,9 @@
regulator-max-microvolt = <19>;
qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <19 19>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <19 19 19>;
qcom,cpr-corners = <19>;
qcom,cpr-corner-fmax-map = <6 12 17 19>;
@@ -137,6 +137,11 @@
<568000 568000 568000 568000 568000
568000 568000 568000 568000 584000
584000 584000 632000 632000 632000
+ 632000 672000 712000 712000>,
+ /* Speed bin 2 */
+ <568000 568000 568000 568000 568000
+ 568000 568000 568000 568000 584000
+ 584000 584000 632000 632000 632000
632000 672000 712000 712000>;
qcom,cpr-floor-to-ceiling-max-range =
@@ -146,13 +151,30 @@
32000 32000 40000 40000>;
qcom,corner-frequencies =
+ /* Speed bin 0 */
<300000000 422400000 499200000
576000000 652800000 748800000
825600000 902400000 979200000
1056000000 1132800000 1209600000
1286400000 1363200000 1440000000
1516800000 1593600000 1651200000
- 1708800000>;
+ 1708800000>,
+ /* Speed bin 1 */
+ <300000000 422400000 499200000
+ 576000000 652800000 748800000
+ 825600000 902400000 979200000
+ 1056000000 1132800000 1209600000
+ 1286400000 1363200000 1440000000
+ 1516800000 1593600000 1651200000
+ 1708800000>,
+ /* Speed bin 2 */
+ <300000000 422400000 499200000
+ 576000000 652800000 748800000
+ 825600000 902400000 979200000
+ 1056000000 1132800000 1209600000
+ 1286400000 1363200000 1440000000
+ 1516800000 1593600000 1670400000
+ 1747200000>;
qcom,cpr-ro-scaling-factor =
<2594 2795 2576 2761 2469 2673 2198
@@ -185,6 +207,8 @@
/* Speed bin 0 */
<0 1 1 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 1 1 1 1 1 1 1>,
+ /* Speed bin 2 */
<0 1 1 1 1 1 1 1>;
qcom,allow-aging-open-loop-voltage-adjustment =
<1>;
@@ -201,20 +225,41 @@
apc0_l3_vreg: regulator {
regulator-name = "apc0_l3_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <11>;
+ regulator-max-microvolt = <13>;
qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <11 11>;
- qcom,cpr-corners = <11>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <11 11 13>;
+ qcom,cpr-corners =
+ /* Speed bin 0 */
+ <11 11 11 11 11 11 11 11>,
+ /* Speed bin 1 */
+ <11 11 11 11 11 11 11 11>,
+ /* Speed bin 2 */
+ <13 13 13 13 13 13 13 13>;
- qcom,cpr-corner-fmax-map = <4 7 9 11>;
+ qcom,cpr-corner-fmax-map =
+ /* Speed bin 0 */
+ <4 7 9 11>,
+ /* Speed bin 1 */
+ <4 7 9 11>,
+ /* Speed bin 2 */
+ <4 7 9 13>;
qcom,cpr-voltage-ceiling =
+ /* Speed bin 0 */
<872000 872000 872000 872000 872000
872000 872000 872000 928000 996000
- 996000>;
+ 996000>,
+ /* Speed bin 1 */
+ <872000 872000 872000 872000 872000
+ 872000 872000 872000 928000 996000
+ 996000>,
+ /* Speed bin 2 */
+ <872000 872000 872000 872000 872000
+ 872000 872000 872000 928000 996000
+ 996000 996000 996000>;
qcom,cpr-voltage-floor =
/* Speed bin 0 */
@@ -224,18 +269,43 @@
/* Speed bin 1 */
<568000 568000 568000 568000 568000
584000 584000 632000 672000 712000
- 712000>;
+ 712000>,
+ /* Speed bin 2 */
+ <568000 568000 568000 568000 568000
+ 584000 584000 632000 672000 712000
+ 712000 712000 712000>;
qcom,cpr-floor-to-ceiling-max-range =
+ /* Speed bin 0 */
<32000 32000 32000 32000 32000
32000 32000 32000 32000 40000
- 40000>;
+ 40000>,
+ /* Speed bin 1 */
+ <32000 32000 32000 32000 32000
+ 32000 32000 32000 32000 40000
+ 40000>,
+ /* Speed bin 2 */
+ <32000 32000 32000 32000 32000
+ 32000 32000 32000 32000 40000
+ 40000 40000 40000>;
qcom,corner-frequencies =
+ /* Speed bin 0 */
<300000000 422400000 499200000
576000000 652800000 729600000
806400000 883200000 960000000
- 1036800000 1094400000>;
+ 1036800000 1094400000>,
+ /* Speed bin 1 */
+ <300000000 422400000 499200000
+ 576000000 652800000 729600000
+ 806400000 883200000 960000000
+ 1036800000 1094400000>,
+ /* Speed bin 2 */
+ <300000000 422400000 499200000
+ 576000000 652800000 729600000
+ 806400000 883200000 960000000
+ 1036800000 1113600000 1209600000
+ 1305600000>;
qcom,cpr-ro-scaling-factor =
<2857 3056 2828 2952 2699 2796 2447
@@ -262,12 +332,14 @@
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <11>;
+ qcom,cpr-aging-ref-corner = <11 11 13>;
qcom,cpr-aging-ro-scaling-factor = <1620>;
qcom,allow-aging-voltage-adjustment =
/* Speed bin 0 */
<0 1 1 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 1 1 1 1 1 1 1>,
+ /* Speed bin 2 */
<0 1 1 1 1 1 1 1>;
qcom,allow-aging-open-loop-voltage-adjustment =
<1>;
@@ -320,7 +392,7 @@
qcom,cpr-panic-reg-name-list =
"APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
- qcom,cpr-aging-ref-voltage = <1056000>;
+ qcom,cpr-aging-ref-voltage = <1136000>;
vdd-supply = <&pm8998_s12>;
thread@0 {
@@ -333,23 +405,27 @@
apc1_perfcl_vreg: regulator {
regulator-name = "apc1_perfcl_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <26>;
+ regulator-max-microvolt = <27>;
qcom,cpr-fuse-corners = <3>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <22 24>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <22 24 25>;
qcom,cpr-corners =
/* Speed bin 0 */
<22 22 22 22 22 22 22 22>,
/* Speed bin 1 */
- <24 24 24 24 24 24 24 24>;
+ <24 24 24 24 24 24 24 24>,
+ /* Speed bin 2 */
+ <25 25 25 25 25 25 25 25>;
qcom,cpr-corner-fmax-map =
/* Speed bin 0 */
<10 17 22>,
/* Speed bin 1 */
- <10 17 24>;
+ <10 17 24>,
+ /* Speed bin 2 */
+ <10 17 25>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -357,13 +433,20 @@
828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
828000 828000 884000 952000 952000
- 1056000 1056000>,
+ 1136000 1136000>,
/* Speed bin 1 */
<828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
828000 828000 884000 952000 952000
- 1056000 1056000 1056000 1056000>;
+ 1136000 1136000 1136000 1136000>,
+ /* Speed bin 2 */
+ <828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 828000 828000 884000 952000 952000
+ 1136000 1136000 1136000 1136000
+ 1136000>;
qcom,cpr-voltage-floor =
/* Speed bin 0 */
@@ -377,7 +460,14 @@
568000 568000 568000 568000 568000
584000 584000 632000 632000 632000
632000 632000 672000 712000 712000
- 772000 772000 772000 772000>;
+ 772000 772000 772000 772000>,
+ /* Speed bin 2 */
+ <568000 568000 568000 568000 568000
+ 568000 568000 568000 568000 568000
+ 584000 584000 632000 632000 632000
+ 632000 632000 672000 712000 712000
+ 772000 772000 772000 772000
+ 772000>;
qcom,cpr-floor-to-ceiling-max-range =
/* Speed bin 0 */
@@ -391,7 +481,13 @@
32000 32000 32000 32000 32000
32000 32000 32000 32000 32000
32000 32000 40000 40000 40000
- 40000 40000 40000 40000>;
+ 40000 40000 40000 40000>,
+ /* Speed bin 2 */
+ <32000 32000 32000 32000 32000
+ 32000 32000 32000 32000 32000
+ 32000 32000 32000 32000 32000
+ 32000 32000 40000 40000 40000
+ 40000 40000 40000 40000 40000>;
qcom,corner-frequencies =
/* Speed bin 0 */
@@ -411,7 +507,17 @@
1267200000 1344000000 1420800000
1497600000 1574400000 1651200000
1728000000 1804800000 1881600000
- 1958400000 2035200000 2092800000>;
+ 1958400000 2035200000 2092800000>,
+ /* Speed bin 2 */
+ <300000000 422400000 499200000
+ 576000000 652800000 729600000
+ 806400000 883200000 960000000
+ 1036800000 1113600000 1190400000
+ 1267200000 1344000000 1420800000
+ 1497600000 1574400000 1651200000
+ 1728000000 1804800000 1881600000
+ 1958400000 2035200000 2112000000
+ 2208000000>;
qcom,cpr-ro-scaling-factor =
<2857 3056 2828 2952 2699 2796 2447
@@ -442,6 +548,15 @@
< 0 0 0>,
< 0 0 0>,
< 0 0 0>,
+ < 0 0 0>,
+ /* Speed bin 2 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
< 0 0 0>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
@@ -462,6 +577,15 @@
< 0 0 0>,
< 0 0 0>,
< 0 0 0>,
+ < 0 0 0>,
+ /* Speed bin 2 */
+ <100000 100000 100000>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
+ < 0 0 0>,
< 0 0 0>;
qcom,allow-voltage-interpolation;
@@ -469,12 +593,14 @@
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <22 24>;
+ qcom,cpr-aging-ref-corner = <22 24 25>;
qcom,cpr-aging-ro-scaling-factor = <1700>;
qcom,allow-aging-voltage-adjustment =
/* Speed bin 0 */
<0 1 1 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 1 1 1 1 1 1 1>,
+ /* Speed bin 2 */
<0 1 1 1 1 1 1 1>;
qcom,allow-aging-open-loop-voltage-adjustment =
<1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index e92bfd9..76c07b7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -35,7 +35,7 @@
sde-vdd-supply = <&mdss_core_gdsc>;
/* interrupt config */
- interrupt-parent = <&intc>;
+ interrupt-parent = <&pdc>;
interrupts = <0 83 0>;
interrupt-controller;
#interrupt-cells = <1>;
@@ -137,6 +137,9 @@
qcom,sde-vbif-size = <0x1040>;
qcom,sde-vbif-id = <0>;
+ qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>;
+ qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>;
+
qcom,sde-inline-rotator = <&mdss_rotator 0>;
qcom,sde-reg-dma-off = <0>;
@@ -261,7 +264,7 @@
interrupt-parent = <&mdss_mdp>;
interrupts = <2 0>;
- qcom,mdss-rot-vbif-qos-setting = <1 1 1 1>;
+ qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>;
qcom,mdss-default-ot-rd-limit = <32>;
qcom,mdss-default-ot-wr-limit = <32>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 6fb6fb8..3870d8f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -67,7 +67,6 @@
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a600000 0xcd00>;
- interrupt-parent = <&intc>;
interrupts = <0 133 0>;
usb-phy = <&qusb_phy0>, <&usb_qmp_dp_phy>;
tx-fifo-resize;
@@ -80,7 +79,6 @@
qcom,usbbam@a704000 {
compatible = "qcom,usb-bam-msm";
reg = <0xa704000 0x17000>;
- interrupt-parent = <&intc>;
interrupts = <0 132 0>;
qcom,bam-type = <0>;
@@ -361,7 +359,6 @@
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a800000 0xcd00>;
- interrupt-parent = <&intc>;
interrupts = <0 138 0>;
usb-phy = <&qusb_phy1>, <&usb_qmp_phy>;
tx-fifo-resize;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index ad451ce..fc90fef 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -30,7 +30,7 @@
model = "Qualcomm Technologies, Inc. SDM845";
compatible = "qcom,sdm845";
qcom,msm-id = <321 0x0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&pdc>;
aliases {
ufshc1 = &ufshc_mem; /* Embedded UFS slot */
@@ -567,6 +567,12 @@
size = <0 0x5c00000>;
};
+ dump_mem: mem_dump_region {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0 0x2400000>;
+ };
+
/* global autoconfigured region for contiguous allocations */
linux,cma {
compatible = "shared-dma-pool";
@@ -600,6 +606,7 @@
reg = <0x17a00000 0x10000>, /* GICD */
<0x17a60000 0x100000>; /* GICR * 8 */
interrupts = <1 9 4>;
+ interrupt-parent = <&intc>;
};
timer {
@@ -801,7 +808,8 @@
< 1881600 >,
< 1958400 >,
< 2035200 >,
- < 2092800 >;
+ < 2092800 >,
+ < 2208000 >;
};
cpubw: qcom,cpubw {
@@ -955,7 +963,9 @@
< 883200 >,
< 960000 >,
< 1036800 >,
- < 1094400 >;
+ < 1094400 >,
+ < 1209600 >,
+ < 1305600 >;
};
l3_cpu4: qcom,l3-cpu4 {
@@ -975,7 +985,9 @@
< 883200 >,
< 960000 >,
< 1036800 >,
- < 1094400 >;
+ < 1094400 >,
+ < 1209600 >,
+ < 1305600 >;
};
devfreq_l3lat_0: qcom,cpu0-l3lat-mon {
@@ -1035,8 +1047,8 @@
cpu-to-dev-map-0 =
< 1708800 762 >;
cpu-to-dev-map-4 =
- < 2035200 762 >,
- < 2092800 2597 >;
+ < 1881600 762 >,
+ < 2208000 2597 >;
};
};
@@ -1134,6 +1146,9 @@
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
vdd-perfcl-supply = <&apc1_perfcl_vreg>;
+ l3-dev0 = <&l3_cpu0>;
+ l3-dev4 = <&l3_cpu4>;
+
qcom,l3-speedbin0-v0 =
< 300000000 0x000c000f 0x00002020 0x1 1 >,
< 422400000 0x50140116 0x00002020 0x1 2 >,
@@ -1158,6 +1173,21 @@
< 1036800000 0x40240936 0x00002b2b 0x3 10 >,
< 1094400000 0x402c0a39 0x00002e2e 0x3 11 >;
+ qcom,l3-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 729600000 0x401c0526 0x00002020 0x1 6 >,
+ < 806400000 0x401c062a 0x00002222 0x1 7 >,
+ < 883200000 0x4024072e 0x00002525 0x2 8 >,
+ < 960000000 0x40240832 0x00002828 0x2 9 >,
+ < 1036800000 0x40240936 0x00002b2b 0x3 10 >,
+ < 1113600000 0x402c0a3a 0x00002e2e 0x3 11 >,
+ < 1209600000 0x402c0b3f 0x00003232 0x3 12 >,
+ < 1305600000 0x40340c44 0x00003636 0x3 13 >;
+
qcom,pwrcl-speedbin0-v0 =
< 300000000 0x000c000f 0x00002020 0x1 1 >,
< 422400000 0x50140116 0x00002020 0x1 2 >,
@@ -1198,6 +1228,27 @@
< 1651200000 0x403c1156 0x00004545 0x3 18 >,
< 1708800000 0x40441259 0x00004747 0x3 19 >;
+ qcom,pwrcl-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 748800000 0x401c0527 0x00002020 0x1 6 >,
+ < 825600000 0x401c062b 0x00002222 0x1 7 >,
+ < 902400000 0x4024072f 0x00002626 0x1 8 >,
+ < 979200000 0x40240833 0x00002929 0x1 9 >,
+ < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
+ < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
+ < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
+ < 1286400000 0x40340c43 0x00003636 0x2 13 >,
+ < 1363200000 0x40340d47 0x00003939 0x2 14 >,
+ < 1440000000 0x40340e4b 0x00003c3c 0x2 15 >,
+ < 1516800000 0x403c0f4f 0x00003f3f 0x2 16 >,
+ < 1593600000 0x403c1053 0x00004242 0x2 17 >,
+ < 1670400000 0x40441157 0x00004646 0x3 18 >,
+ < 1747200000 0x4044125b 0x00004949 0x3 19 >;
+
qcom,perfcl-speedbin0-v0 =
< 300000000 0x000c000f 0x00002020 0x1 1 >,
< 422400000 0x50140116 0x00002020 0x1 2 >,
@@ -1248,6 +1299,33 @@
< 2035200000 0x404c166a 0x00005555 0x3 23 >,
< 2092800000 0x4054176d 0x00005757 0x3 24 >;
+ qcom,perfcl-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 729600000 0x401c0526 0x00002020 0x1 6 >,
+ < 806400000 0x401c062a 0x00002222 0x1 7 >,
+ < 883200000 0x4024072e 0x00002525 0x1 8 >,
+ < 960000000 0x40240832 0x00002828 0x1 9 >,
+ < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
+ < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
+ < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
+ < 1267200000 0x40340c42 0x00003535 0x2 13 >,
+ < 1344000000 0x40340d46 0x00003838 0x2 14 >,
+ < 1420800000 0x40340e4a 0x00003b3b 0x2 15 >,
+ < 1497600000 0x403c0f4e 0x00003e3e 0x2 16 >,
+ < 1574400000 0x403c1052 0x00004242 0x2 17 >,
+ < 1651200000 0x403c1156 0x00004545 0x2 18 >,
+ < 1728000000 0x4044125a 0x00004848 0x3 19 >,
+ < 1804800000 0x4044135e 0x00004b4b 0x3 20 >,
+ < 1881600000 0x404c1462 0x00004e4e 0x3 21 >,
+ < 1958400000 0x404c1566 0x00005252 0x3 22 >,
+ < 2035200000 0x404c166a 0x00005555 0x3 23 >,
+ < 2112000000 0x4054176e 0x00005858 0x3 24 >,
+ < 2208000000 0x40541873 0x00005c5c 0x3 25 >;
+
qcom,l3-min-cpr-vc-bin0 = <7>;
qcom,pwrcl-min-cpr-vc-bin0 = <6>;
qcom,perfcl-min-cpr-vc-bin0 = <7>;
@@ -1712,7 +1790,7 @@
qcom,ssc@5c00000 {
compatible = "qcom,pil-tz-generic";
reg = <0x5c00000 0x4000>;
- interrupts = <0 377 1>;
+ interrupts = <0 494 1>;
vdd_cx-supply = <&pm8998_l27_level>;
vdd_px-supply = <&pm8998_lvs2>;
@@ -3912,6 +3990,41 @@
#thermal-sensor-cells = <1>;
};
+ mem_dump {
+ compatible = "qcom,mem-dump";
+ memory-region = <&dump_mem>;
+
+ rpmh_dump {
+ qcom,dump-size = <0x2000000>;
+ qcom,dump-id = <0xec>;
+ };
+
+ rpm_sw_dump {
+ qcom,dump-size = <0x28000>;
+ qcom,dump-id = <0xea>;
+ };
+
+ pmic_dump {
+ qcom,dump-size = <0x10000>;
+ qcom,dump-id = <0xe4>;
+ };
+
+ tmc_etf_dump {
+ qcom,dump-size = <0x10000>;
+ qcom,dump-id = <0xf0>;
+ };
+
+ tmc_etf_swao_dump {
+ qcom,dump-size = <0x8400>;
+ qcom,dump-id = <0xf1>;
+ };
+
+ misc_data_dump {
+ qcom,dump-size = <0x1000>;
+ qcom,dump-id = <0xe8>;
+ };
+ };
+
gpi_dma0: qcom,gpi-dma@0x800000 {
#dma-cells = <6>;
compatible = "qcom,gpi-dma";
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 19a6db8..12b1f09 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -547,6 +547,7 @@
CONFIG_CORESIGHT_DUMMY=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_XCBC=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 04a0d3e..23b1215 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -620,6 +620,7 @@
CONFIG_CORESIGHT_DUMMY=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_XCBC=y
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 4e0497f..0fe7e43 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -42,25 +42,35 @@
#define __smp_rmb() dmb(ishld)
#define __smp_wmb() dmb(ishst)
-#define __smp_store_release(p, v) \
+#define __smp_store_release(p, v) \
do { \
+ union { typeof(*p) __val; char __c[1]; } __u = \
+ { .__val = (__force typeof(*p)) (v) }; \
compiletime_assert_atomic_type(*p); \
switch (sizeof(*p)) { \
case 1: \
asm volatile ("stlrb %w1, %0" \
- : "=Q" (*p) : "r" (v) : "memory"); \
+ : "=Q" (*p) \
+ : "r" (*(__u8 *)__u.__c) \
+ : "memory"); \
break; \
case 2: \
asm volatile ("stlrh %w1, %0" \
- : "=Q" (*p) : "r" (v) : "memory"); \
+ : "=Q" (*p) \
+ : "r" (*(__u16 *)__u.__c) \
+ : "memory"); \
break; \
case 4: \
asm volatile ("stlr %w1, %0" \
- : "=Q" (*p) : "r" (v) : "memory"); \
+ : "=Q" (*p) \
+ : "r" (*(__u32 *)__u.__c) \
+ : "memory"); \
break; \
case 8: \
asm volatile ("stlr %1, %0" \
- : "=Q" (*p) : "r" (v) : "memory"); \
+ : "=Q" (*p) \
+ : "r" (*(__u64 *)__u.__c) \
+ : "memory"); \
break; \
} \
} while (0)
diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h
index 91b26d2..ae852ad 100644
--- a/arch/arm64/include/asm/cmpxchg.h
+++ b/arch/arm64/include/asm/cmpxchg.h
@@ -46,7 +46,7 @@
" swp" #acq_lse #rel #sz "\t%" #w "3, %" #w "0, %2\n" \
__nops(3) \
" " #nop_lse) \
- : "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr) \
+ : "=&r" (ret), "=&r" (tmp), "+Q" (*(unsigned long *)ptr) \
: "r" (x) \
: cl); \
\
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 73fee2c..21934d1 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -98,11 +98,12 @@
*/
#define __range_ok(addr, size) \
({ \
+ unsigned long __addr = (unsigned long __force)(addr); \
unsigned long flag, roksum; \
__chk_user_ptr(addr); \
asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls" \
: "=&r" (flag), "=&r" (roksum) \
- : "1" (addr), "Ir" (size), \
+ : "1" (__addr), "Ir" (size), \
"r" (current_thread_info()->addr_limit) \
: "cc"); \
flag; \
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index bdb35b9..29d2ad8 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -306,7 +306,8 @@
_ASM_EXTABLE(0b, 4b) \
_ASM_EXTABLE(1b, 4b) \
: "=&r" (res), "+r" (data), "=&r" (temp), "=&r" (temp2) \
- : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT), \
+ : "r" ((unsigned long)addr), "i" (-EAGAIN), \
+ "i" (-EFAULT), \
"i" (__SWP_LL_SC_LOOPS) \
: "memory"); \
uaccess_disable(); \
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
index aaf42ae..14c4e3b 100644
--- a/arch/arm64/kvm/hyp/Makefile
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -2,6 +2,8 @@
# Makefile for Kernel-based Virtual Machine module, HYP part
#
+ccflags-y += -fno-stack-protector
+
KVM=../../../../virt/kvm
obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v2-sr.o
diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h
index 07238b3..3db3812 100644
--- a/arch/metag/include/asm/uaccess.h
+++ b/arch/metag/include/asm/uaccess.h
@@ -28,24 +28,32 @@
#define segment_eq(a, b) ((a).seg == (b).seg)
-#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
-/*
- * Explicitly allow NULL pointers here. Parts of the kernel such
- * as readv/writev use access_ok to validate pointers, but want
- * to allow NULL pointers for various reasons. NULL pointers are
- * safe to allow through because the first page is not mappable on
- * Meta.
- *
- * We also wish to avoid letting user code access the system area
- * and the kernel half of the address space.
- */
-#define __user_bad(addr, size) (((addr) > 0 && (addr) < META_MEMORY_BASE) || \
- ((addr) > PAGE_OFFSET && \
- (addr) < LINCORE_BASE))
-
static inline int __access_ok(unsigned long addr, unsigned long size)
{
- return __kernel_ok || !__user_bad(addr, size);
+ /*
+ * Allow access to the user mapped memory area, but not the system area
+ * before it. The check extends to the top of the address space when
+ * kernel access is allowed (there's no real reason to user copy to the
+ * system area in any case).
+ */
+ if (likely(addr >= META_MEMORY_BASE && addr < get_fs().seg &&
+ size <= get_fs().seg - addr))
+ return true;
+ /*
+ * Explicitly allow NULL pointers here. Parts of the kernel such
+ * as readv/writev use access_ok to validate pointers, but want
+ * to allow NULL pointers for various reasons. NULL pointers are
+ * safe to allow through because the first page is not mappable on
+ * Meta.
+ */
+ if (!addr)
+ return true;
+ /* Allow access to core code memory area... */
+ if (addr >= LINCORE_CODE_BASE && addr <= LINCORE_CODE_LIMIT &&
+ size <= LINCORE_CODE_LIMIT + 1 - addr)
+ return true;
+ /* ... but no other areas. */
+ return false;
}
#define access_ok(type, addr, size) __access_ok((unsigned long)(addr), \
@@ -186,8 +194,13 @@
extern long __must_check __strncpy_from_user(char *dst, const char __user *src,
long count);
-#define strncpy_from_user(dst, src, count) __strncpy_from_user(dst, src, count)
-
+static inline long
+strncpy_from_user(char *dst, const char __user *src, long count)
+{
+ if (!access_ok(VERIFY_READ, src, 1))
+ return -EFAULT;
+ return __strncpy_from_user(dst, src, count);
+}
/*
* Return the size of a string (including the ending 0)
*
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 5a4f2eb..5e844f6 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1368,6 +1368,7 @@
select WEAK_ORDERING
select WEAK_REORDERING_BEYOND_LLSC
select MIPS_PGD_C0_CONTEXT
+ select MIPS_L1_CACHE_SHIFT_6
select GPIOLIB
help
The Loongson 3 processor implements the MIPS64R2 instruction
diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
index b9e3f0a..0012f03 100644
--- a/arch/powerpc/include/asm/mmu_context.h
+++ b/arch/powerpc/include/asm/mmu_context.h
@@ -70,8 +70,9 @@
* switch_mm is the entry point called from the architecture independent
* code in kernel/sched/core.c
*/
-static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
- struct task_struct *tsk)
+static inline void switch_mm_irqs_off(struct mm_struct *prev,
+ struct mm_struct *next,
+ struct task_struct *tsk)
{
/* Mark this context has been used on the new CPU */
if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next)))
@@ -110,6 +111,18 @@
switch_mmu_context(prev, next, tsk);
}
+static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ switch_mm_irqs_off(prev, next, tsk);
+ local_irq_restore(flags);
+}
+#define switch_mm_irqs_off switch_mm_irqs_off
+
+
#define deactivate_mm(tsk,mm) do { } while (0)
/*
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index a5dd493..6ef8f0b 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -724,7 +724,7 @@
*/
#define MAX_WAIT_FOR_RECOVERY 300
-static void eeh_handle_normal_event(struct eeh_pe *pe)
+static bool eeh_handle_normal_event(struct eeh_pe *pe)
{
struct pci_bus *frozen_bus;
struct eeh_dev *edev, *tmp;
@@ -736,7 +736,7 @@
if (!frozen_bus) {
pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
__func__, pe->phb->global_number, pe->addr);
- return;
+ return false;
}
eeh_pe_update_time_stamp(pe);
@@ -870,7 +870,7 @@
pr_info("EEH: Notify device driver to resume\n");
eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
- return;
+ return false;
excess_failures:
/*
@@ -915,8 +915,12 @@
pci_lock_rescan_remove();
pci_hp_remove_devices(frozen_bus);
pci_unlock_rescan_remove();
+
+ /* The passed PE should no longer be used */
+ return true;
}
}
+ return false;
}
static void eeh_handle_special_event(void)
@@ -982,7 +986,14 @@
*/
if (rc == EEH_NEXT_ERR_FROZEN_PE ||
rc == EEH_NEXT_ERR_FENCED_PHB) {
- eeh_handle_normal_event(pe);
+ /*
+ * eeh_handle_normal_event() can make the PE stale if it
+ * determines that the PE cannot possibly be recovered.
+ * Don't modify the PE state if that's the case.
+ */
+ if (eeh_handle_normal_event(pe))
+ continue;
+
eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
} else {
pci_lock_rescan_remove();
diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S
index 38a1f96..ca03eb2 100644
--- a/arch/powerpc/kernel/exceptions-64e.S
+++ b/arch/powerpc/kernel/exceptions-64e.S
@@ -735,8 +735,14 @@
andis. r15,r14,(DBSR_IC|DBSR_BT)@h
beq+ 1f
+#ifdef CONFIG_RELOCATABLE
+ ld r15,PACATOC(r13)
+ ld r14,interrupt_base_book3e@got(r15)
+ ld r15,__end_interrupts@got(r15)
+#else
LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e)
LOAD_REG_IMMEDIATE(r15,__end_interrupts)
+#endif
cmpld cr0,r10,r14
cmpld cr1,r10,r15
blt+ cr0,1f
@@ -799,8 +805,14 @@
andis. r15,r14,(DBSR_IC|DBSR_BT)@h
beq+ 1f
+#ifdef CONFIG_RELOCATABLE
+ ld r15,PACATOC(r13)
+ ld r14,interrupt_base_book3e@got(r15)
+ ld r15,__end_interrupts@got(r15)
+#else
LOAD_REG_IMMEDIATE(r14,interrupt_base_book3e)
LOAD_REG_IMMEDIATE(r15,__end_interrupts)
+#endif
cmpld cr0,r10,r14
cmpld cr1,r10,r15
blt+ cr0,1f
diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c
index 5e7ece0..ea236bf 100644
--- a/arch/powerpc/kernel/mce.c
+++ b/arch/powerpc/kernel/mce.c
@@ -205,6 +205,8 @@
{
int index;
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
+
/*
* For now just print it to console.
* TODO: log this error event to FSP or nvram.
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 49a680d..c716473 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -839,6 +839,25 @@
if (!MSR_TM_SUSPENDED(mfmsr()))
return;
+ /*
+ * If we are in a transaction and FP is off then we can't have
+ * used FP inside that transaction. Hence the checkpointed
+ * state is the same as the live state. We need to copy the
+ * live state to the checkpointed state so that when the
+ * transaction is restored, the checkpointed state is correct
+ * and the aborted transaction sees the correct state. We use
+ * ckpt_regs.msr here as that's what tm_reclaim will use to
+ * determine if it's going to write the checkpointed state or
+ * not. So either this will write the checkpointed registers,
+ * or reclaim will. Similarly for VMX.
+ */
+ if ((thr->ckpt_regs.msr & MSR_FP) == 0)
+ memcpy(&thr->ckfp_state, &thr->fp_state,
+ sizeof(struct thread_fp_state));
+ if ((thr->ckpt_regs.msr & MSR_VEC) == 0)
+ memcpy(&thr->ckvr_state, &thr->vr_state,
+ sizeof(struct thread_vr_state));
+
giveup_all(container_of(thr, struct task_struct, thread));
tm_reclaim(thr, thr->ckpt_regs.msr, cause);
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 023a462..43021f8 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -302,8 +302,6 @@
__this_cpu_inc(irq_stat.mce_exceptions);
- add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
-
if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
handled = cur_cpu_spec->machine_check_early(regs);
return handled;
@@ -737,6 +735,8 @@
__this_cpu_inc(irq_stat.mce_exceptions);
+ add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
+
/* See if any machine dependent calls. In theory, we would want
* to call the CPU first, and call the ppc_md. one if the CPU
* one returns a positive number. However there is existing code
diff --git a/arch/powerpc/mm/mmu_context_iommu.c b/arch/powerpc/mm/mmu_context_iommu.c
index 7de7124..fd59680 100644
--- a/arch/powerpc/mm/mmu_context_iommu.c
+++ b/arch/powerpc/mm/mmu_context_iommu.c
@@ -81,7 +81,7 @@
gfp_t gfp_mask = GFP_USER;
struct page *new_page;
- if (PageHuge(page) || PageTransHuge(page) || PageCompound(page))
+ if (PageCompound(page))
return NULL;
if (PageHighMem(page))
@@ -100,7 +100,7 @@
LIST_HEAD(cma_migrate_pages);
/* Ignore huge pages for now */
- if (PageHuge(page) || PageTransHuge(page) || PageCompound(page))
+ if (PageCompound(page))
return -EBUSY;
lru_add_drain();
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index 423e450..72ae2cd 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -288,7 +288,6 @@
if (rc)
return rc;
- of_node_put(dn); /* Must decrement the refcount */
return 0;
}
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index 408b4f4..5982544 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -427,6 +427,20 @@
}
/*
+ * Initialize final note (needed for /proc/vmcore code)
+ */
+static void *nt_final(void *ptr)
+{
+ Elf64_Nhdr *note;
+
+ note = (Elf64_Nhdr *) ptr;
+ note->n_namesz = 0;
+ note->n_descsz = 0;
+ note->n_type = 0;
+ return PTR_ADD(ptr, sizeof(Elf64_Nhdr));
+}
+
+/*
* Initialize ELF header (new kernel)
*/
static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
@@ -513,6 +527,7 @@
if (sa->prefix != 0)
ptr = fill_cpu_elf_notes(ptr, cpu++, sa);
ptr = nt_vmcoreinfo(ptr);
+ ptr = nt_final(ptr);
memset(phdr, 0, sizeof(*phdr));
phdr->p_type = PT_NOTE;
phdr->p_offset = notes_offset;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 49a3073..c438168 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -321,6 +321,7 @@
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+.Lsysc_exit_timer:
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
lmg %r11,%r15,__PT_R11(%r11)
@@ -606,6 +607,7 @@
lg %r14,__LC_VDSO_PER_CPU
lmg %r0,%r10,__PT_R0(%r11)
mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+.Lio_exit_timer:
stpt __LC_EXIT_TIMER
mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
lmg %r11,%r15,__PT_R11(%r11)
@@ -1135,15 +1137,23 @@
br %r14
.Lcleanup_sysc_restore:
+ # check if stpt has been executed
clg %r9,BASED(.Lcleanup_sysc_restore_insn)
+ jh 0f
+ mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
+ cghi %r11,__LC_SAVE_AREA_ASYNC
je 0f
+ mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: clg %r9,BASED(.Lcleanup_sysc_restore_insn+8)
+ je 1f
lg %r9,24(%r11) # get saved pointer to pt_regs
mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
-0: lmg %r8,%r9,__LC_RETURN_PSW
+1: lmg %r8,%r9,__LC_RETURN_PSW
br %r14
.Lcleanup_sysc_restore_insn:
+ .quad .Lsysc_exit_timer
.quad .Lsysc_done - 4
.Lcleanup_io_tif:
@@ -1151,15 +1161,20 @@
br %r14
.Lcleanup_io_restore:
+ # check if stpt has been executed
clg %r9,BASED(.Lcleanup_io_restore_insn)
- je 0f
+ jh 0f
+ mvc __LC_EXIT_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: clg %r9,BASED(.Lcleanup_io_restore_insn+8)
+ je 1f
lg %r9,24(%r11) # get saved r11 pointer to pt_regs
mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
mvc 0(64,%r11),__PT_R8(%r9)
lmg %r0,%r7,__PT_R0(%r9)
-0: lmg %r8,%r9,__LC_RETURN_PSW
+1: lmg %r8,%r9,__LC_RETURN_PSW
br %r14
.Lcleanup_io_restore_insn:
+ .quad .Lio_exit_timer
.quad .Lio_done - 4
.Lcleanup_idle:
diff --git a/arch/um/kernel/initrd.c b/arch/um/kernel/initrd.c
index 48bae81..6f6e789 100644
--- a/arch/um/kernel/initrd.c
+++ b/arch/um/kernel/initrd.c
@@ -14,7 +14,7 @@
static char *initrd __initdata = NULL;
static int load_initrd(char *filename, void *buf, int size);
-static int __init read_initrd(void)
+int __init read_initrd(void)
{
void *area;
long long size;
@@ -46,8 +46,6 @@
return 0;
}
-__uml_postsetup(read_initrd);
-
static int __init uml_initrd_setup(char *line, int *add)
{
initrd = line;
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index e8175a8..26b47de 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -336,11 +336,17 @@
return start_uml();
}
+int __init __weak read_initrd(void)
+{
+ return 0;
+}
+
void __init setup_arch(char **cmdline_p)
{
stack_protections((unsigned long) &init_thread_info);
setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
mem_total_pages(physmem_size, iomem_size, highmem);
+ read_initrd();
paging_init();
strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index faf3687..a300aa1 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -315,10 +315,10 @@
#define __get_user_asm_u64(x, ptr, retval, errret) \
({ \
__typeof__(ptr) __ptr = (ptr); \
- asm volatile(ASM_STAC "\n" \
+ asm volatile("\n" \
"1: movl %2,%%eax\n" \
"2: movl %3,%%edx\n" \
- "3: " ASM_CLAC "\n" \
+ "3:\n" \
".section .fixup,\"ax\"\n" \
"4: mov %4,%0\n" \
" xorl %%eax,%%eax\n" \
@@ -327,7 +327,7 @@
".previous\n" \
_ASM_EXTABLE(1b, 4b) \
_ASM_EXTABLE(2b, 4b) \
- : "=r" (retval), "=A"(x) \
+ : "=r" (retval), "=&A"(x) \
: "m" (__m(__ptr)), "m" __m(((u32 *)(__ptr)) + 1), \
"i" (errret), "0" (retval)); \
})
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
index 2f2b8c7..6f0ab305 100644
--- a/arch/x86/kernel/fpu/init.c
+++ b/arch/x86/kernel/fpu/init.c
@@ -101,6 +101,7 @@
* Boot time FPU feature detection code:
*/
unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
+EXPORT_SYMBOL_GPL(mxcsr_feature_mask);
static void __init fpu__init_system_mxcsr(void)
{
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 43c1528..81bba3c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1735,6 +1735,7 @@
{
struct kvm_arch *ka = &kvm->arch;
struct pvclock_vcpu_time_info hv_clock;
+ u64 ret;
spin_lock(&ka->pvclock_gtod_sync_lock);
if (!ka->use_master_clock) {
@@ -1746,10 +1747,17 @@
hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
spin_unlock(&ka->pvclock_gtod_sync_lock);
+ /* both __this_cpu_read() and rdtsc() should be on the same cpu */
+ get_cpu();
+
kvm_get_time_scale(NSEC_PER_SEC, __this_cpu_read(cpu_tsc_khz) * 1000LL,
&hv_clock.tsc_shift,
&hv_clock.tsc_to_system_mul);
- return __pvclock_read_cycles(&hv_clock, rdtsc());
+ ret = __pvclock_read_cycles(&hv_clock, rdtsc());
+
+ put_cpu();
+
+ return ret;
}
u64 get_kvmclock_ns(struct kvm *kvm)
@@ -3231,11 +3239,14 @@
}
}
+#define XSAVE_MXCSR_OFFSET 24
+
static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
struct kvm_xsave *guest_xsave)
{
u64 xstate_bv =
*(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)];
+ u32 mxcsr = *(u32 *)&guest_xsave->region[XSAVE_MXCSR_OFFSET / sizeof(u32)];
if (boot_cpu_has(X86_FEATURE_XSAVE)) {
/*
@@ -3243,11 +3254,13 @@
* CPUID leaf 0xD, index 0, EDX:EAX. This is for compatibility
* with old userspace.
*/
- if (xstate_bv & ~kvm_supported_xcr0())
+ if (xstate_bv & ~kvm_supported_xcr0() ||
+ mxcsr & ~mxcsr_feature_mask)
return -EINVAL;
load_xsave(vcpu, (u8 *)guest_xsave->region);
} else {
- if (xstate_bv & ~XFEATURE_MASK_FPSSE)
+ if (xstate_bv & ~XFEATURE_MASK_FPSSE ||
+ mxcsr & ~mxcsr_feature_mask)
return -EINVAL;
memcpy(&vcpu->arch.guest_fpu.state.fxsave,
guest_xsave->region, sizeof(struct fxregs_state));
@@ -4750,16 +4763,20 @@
static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
{
- /* TODO: String I/O for in kernel device */
- int r;
+ int r = 0, i;
- if (vcpu->arch.pio.in)
- r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port,
- vcpu->arch.pio.size, pd);
- else
- r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
- vcpu->arch.pio.port, vcpu->arch.pio.size,
- pd);
+ for (i = 0; i < vcpu->arch.pio.count; i++) {
+ if (vcpu->arch.pio.in)
+ r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port,
+ vcpu->arch.pio.size, pd);
+ else
+ r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
+ vcpu->arch.pio.port, vcpu->arch.pio.size,
+ pd);
+ if (r)
+ break;
+ pd += vcpu->arch.pio.size;
+ }
return r;
}
@@ -4797,6 +4814,8 @@
if (vcpu->arch.pio.count)
goto data_avail;
+ memset(vcpu->arch.pio_data, 0, size * count);
+
ret = emulator_pio_in_out(vcpu, size, port, val, count, true);
if (ret) {
data_avail:
diff --git a/drivers/Makefile b/drivers/Makefile
index 990f63c..d0abb5a 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -73,6 +73,7 @@
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
+obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SCSI) += scsi/
obj-y += nvme/
obj-$(CONFIG_ATA) += ata/
@@ -131,7 +132,6 @@
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
-obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
obj-y += clocksource/
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 4256d9b..b0beb52 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -206,6 +206,7 @@
buf->data = dbuf;
buf->allocated_size = size;
init_completion(&buf->completion);
+ INIT_LIST_HEAD(&buf->list);
#ifdef CONFIG_FW_LOADER_USER_HELPER
INIT_LIST_HEAD(&buf->pending_list);
#endif
diff --git a/drivers/char/diag/diagfwd_glink.h b/drivers/char/diag/diagfwd_glink.h
index bad4629..73f2fe8 100644
--- a/drivers/char/diag/diagfwd_glink.h
+++ b/drivers/char/diag/diagfwd_glink.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-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
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index c4094c4..34ef474 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -859,7 +859,11 @@
} else if (!strcmp(str, "auto")) {
parport_nr[0] = LP_PARPORT_AUTO;
} else if (!strcmp(str, "none")) {
- parport_nr[parport_ptr++] = LP_PARPORT_NONE;
+ if (parport_ptr < LP_NO)
+ parport_nr[parport_ptr++] = LP_PARPORT_NONE;
+ else
+ printk(KERN_INFO "lp: too many ports, %s ignored.\n",
+ str);
} else if (!strcmp(str, "reset")) {
reset = 1;
}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 7e4a9d1..6e0cbe0 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -340,6 +340,11 @@
static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
size_t size = vma->vm_end - vma->vm_start;
+ phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
+
+ /* It's illegal to wrap around the end of the physical address space. */
+ if (offset + (phys_addr_t)size < offset)
+ return -EINVAL;
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
return -EINVAL;
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index a7c870a..fa0f668 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -111,8 +111,7 @@
memcpy_fromio(buf, priv->rsp, 6);
expected = be32_to_cpup((__be32 *) &buf[2]);
-
- if (expected > count)
+ if (expected > count || expected < 6)
return -EIO;
memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index e3a9155..c642877 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -49,9 +49,10 @@
*/
#define TPM_I2C_MAX_BUF_SIZE 32
#define TPM_I2C_RETRY_COUNT 32
-#define TPM_I2C_BUS_DELAY 1 /* msec */
-#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
-#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
+#define TPM_I2C_BUS_DELAY 1000 /* usec */
+#define TPM_I2C_RETRY_DELAY_SHORT (2 * 1000) /* usec */
+#define TPM_I2C_RETRY_DELAY_LONG (10 * 1000) /* usec */
+#define TPM_I2C_DELAY_RANGE 300 /* usec */
#define OF_IS_TPM2 ((void *)1)
#define I2C_IS_TPM2 1
@@ -123,7 +124,9 @@
/* this causes the current command to be aborted */
for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
- msleep(TPM_I2C_BUS_DELAY);
+ if (status < 0)
+ usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
+ + TPM_I2C_DELAY_RANGE);
}
return status;
}
@@ -160,7 +163,8 @@
burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
break;
}
- msleep(TPM_I2C_BUS_DELAY);
+ usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY
+ + TPM_I2C_DELAY_RANGE);
} while (time_before(jiffies, stop));
return burst_count;
@@ -203,13 +207,17 @@
return 0;
/* use polling to wait for the event */
- ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
+ ten_msec = jiffies + usecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
stop = jiffies + timeout;
do {
if (time_before(jiffies, ten_msec))
- msleep(TPM_I2C_RETRY_DELAY_SHORT);
+ usleep_range(TPM_I2C_RETRY_DELAY_SHORT,
+ TPM_I2C_RETRY_DELAY_SHORT
+ + TPM_I2C_DELAY_RANGE);
else
- msleep(TPM_I2C_RETRY_DELAY_LONG);
+ usleep_range(TPM_I2C_RETRY_DELAY_LONG,
+ TPM_I2C_RETRY_DELAY_LONG
+ + TPM_I2C_DELAY_RANGE);
status_valid = i2c_nuvoton_check_status(chip, mask,
value);
if (status_valid)
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index a1ce060..4d24ec3 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -160,8 +160,10 @@
u32 value;
/* wait for burstcount */
- /* which timeout value, spec has 2 answers (c & d) */
- stop = jiffies + chip->timeout_d;
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ stop = jiffies + chip->timeout_a;
+ else
+ stop = jiffies + chip->timeout_d;
do {
rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value);
if (rc < 0)
diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
index dbaad9c..3b97b14 100644
--- a/drivers/char/tpm/tpm_tis_spi.c
+++ b/drivers/char/tpm/tpm_tis_spi.c
@@ -48,8 +48,8 @@
struct tpm_tis_data priv;
struct spi_device *spi_device;
- u8 tx_buf[MAX_SPI_FRAMESIZE + 4];
- u8 rx_buf[MAX_SPI_FRAMESIZE + 4];
+ u8 tx_buf[4];
+ u8 rx_buf[4];
};
static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
@@ -57,120 +57,96 @@
return container_of(data, struct tpm_tis_spi_phy, priv);
}
-static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *result)
+static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *buffer, u8 direction)
{
struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
- int ret, i;
+ int ret = 0;
+ int i;
struct spi_message m;
- struct spi_transfer spi_xfer = {
- .tx_buf = phy->tx_buf,
- .rx_buf = phy->rx_buf,
- .len = 4,
- };
-
- if (len > MAX_SPI_FRAMESIZE)
- return -ENOMEM;
-
- phy->tx_buf[0] = 0x80 | (len - 1);
- phy->tx_buf[1] = 0xd4;
- phy->tx_buf[2] = (addr >> 8) & 0xFF;
- phy->tx_buf[3] = addr & 0xFF;
-
- spi_xfer.cs_change = 1;
- spi_message_init(&m);
- spi_message_add_tail(&spi_xfer, &m);
+ struct spi_transfer spi_xfer;
+ u8 transfer_len;
spi_bus_lock(phy->spi_device->master);
- ret = spi_sync_locked(phy->spi_device, &m);
- if (ret < 0)
- goto exit;
- memset(phy->tx_buf, 0, len);
+ while (len) {
+ transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
- /* According to TCG PTP specification, if there is no TPM present at
- * all, then the design has a weak pull-up on MISO. If a TPM is not
- * present, a pull-up on MISO means that the SB controller sees a 1,
- * and will latch in 0xFF on the read.
- */
- for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
- spi_xfer.len = 1;
+ phy->tx_buf[0] = direction | (transfer_len - 1);
+ phy->tx_buf[1] = 0xd4;
+ phy->tx_buf[2] = addr >> 8;
+ phy->tx_buf[3] = addr;
+
+ memset(&spi_xfer, 0, sizeof(spi_xfer));
+ spi_xfer.tx_buf = phy->tx_buf;
+ spi_xfer.rx_buf = phy->rx_buf;
+ spi_xfer.len = 4;
+ spi_xfer.cs_change = 1;
+
spi_message_init(&m);
spi_message_add_tail(&spi_xfer, &m);
ret = spi_sync_locked(phy->spi_device, &m);
if (ret < 0)
goto exit;
+
+ if ((phy->rx_buf[3] & 0x01) == 0) {
+ // handle SPI wait states
+ phy->tx_buf[0] = 0;
+
+ for (i = 0; i < TPM_RETRY; i++) {
+ spi_xfer.len = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+ if (phy->rx_buf[0] & 0x01)
+ break;
+ }
+
+ if (i == TPM_RETRY) {
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+ }
+
+ spi_xfer.cs_change = 0;
+ spi_xfer.len = transfer_len;
+ spi_xfer.delay_usecs = 5;
+
+ if (direction) {
+ spi_xfer.tx_buf = NULL;
+ spi_xfer.rx_buf = buffer;
+ } else {
+ spi_xfer.tx_buf = buffer;
+ spi_xfer.rx_buf = NULL;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+
+ len -= transfer_len;
+ buffer += transfer_len;
}
- spi_xfer.cs_change = 0;
- spi_xfer.len = len;
- spi_xfer.rx_buf = result;
-
- spi_message_init(&m);
- spi_message_add_tail(&spi_xfer, &m);
- ret = spi_sync_locked(phy->spi_device, &m);
-
exit:
spi_bus_unlock(phy->spi_device->master);
return ret;
}
+static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *result)
+{
+ return tpm_tis_spi_transfer(data, addr, len, result, 0x80);
+}
+
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *value)
{
- struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
- int ret, i;
- struct spi_message m;
- struct spi_transfer spi_xfer = {
- .tx_buf = phy->tx_buf,
- .rx_buf = phy->rx_buf,
- .len = 4,
- };
-
- if (len > MAX_SPI_FRAMESIZE)
- return -ENOMEM;
-
- phy->tx_buf[0] = len - 1;
- phy->tx_buf[1] = 0xd4;
- phy->tx_buf[2] = (addr >> 8) & 0xFF;
- phy->tx_buf[3] = addr & 0xFF;
-
- spi_xfer.cs_change = 1;
- spi_message_init(&m);
- spi_message_add_tail(&spi_xfer, &m);
-
- spi_bus_lock(phy->spi_device->master);
- ret = spi_sync_locked(phy->spi_device, &m);
- if (ret < 0)
- goto exit;
-
- memset(phy->tx_buf, 0, len);
-
- /* According to TCG PTP specification, if there is no TPM present at
- * all, then the design has a weak pull-up on MISO. If a TPM is not
- * present, a pull-up on MISO means that the SB controller sees a 1,
- * and will latch in 0xFF on the read.
- */
- for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
- spi_xfer.len = 1;
- spi_message_init(&m);
- spi_message_add_tail(&spi_xfer, &m);
- ret = spi_sync_locked(phy->spi_device, &m);
- if (ret < 0)
- goto exit;
- }
-
- spi_xfer.len = len;
- spi_xfer.tx_buf = value;
- spi_xfer.cs_change = 0;
- spi_xfer.tx_buf = value;
- spi_message_init(&m);
- spi_message_add_tail(&spi_xfer, &m);
- ret = spi_sync_locked(phy->spi_device, &m);
-
-exit:
- spi_bus_unlock(phy->spi_device->master);
- return ret;
+ return tpm_tis_spi_transfer(data, addr, len, value, 0);
}
static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index d15d1bb..fd3617b 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -59,7 +59,6 @@
#define FABIA_USER_CTL_LO 0xc
#define FABIA_USER_CTL_HI 0x10
-#define FABIA_CAL_L_VAL 0x8
#define FABIA_FRAC_VAL 0x38
#define FABIA_OPMODE 0x2c
#define FABIA_PLL_STANDBY 0x0
@@ -463,12 +462,9 @@
{
u32 val, mask;
- if (config->l) {
+ if (config->l)
regmap_write(regmap, pll->offset + PLL_L_VAL,
config->l);
- regmap_write(regmap, pll->offset + FABIA_CAL_L_VAL,
- config->l);
- }
if (config->frac)
regmap_write(regmap, pll->offset + FABIA_FRAC_VAL,
@@ -627,12 +623,6 @@
}
regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
- /*
- * pll_cal_l_val is set to pll_l_val on MOST targets. Set it
- * explicitly here for PLL out-of-reset calibration to work
- * without a glitch on ALL of them.
- */
- regmap_write(pll->clkr.regmap, off + FABIA_CAL_L_VAL, l);
regmap_write(pll->clkr.regmap, off + FABIA_FRAC_VAL, a);
/* Latch the PLL input */
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 4efecef..3a0677f 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -1737,6 +1737,8 @@
struct device *cpu_dev;
struct clk_osm *c, *parent;
struct clk_hw *hw_parent;
+ struct device_node *l3_node_0, *l3_node_4;
+ struct platform_device *l3_dev_0, *l3_dev_4;
for_each_possible_cpu(cpu) {
c = logical_cpu_to_clk(cpu);
@@ -1754,7 +1756,35 @@
dev_name(cpu_dev));
}
- /*TODO: Figure out which device to tag the L3 table to */
+ l3_node_0 = of_parse_phandle(pdev->dev.of_node, "l3-dev0", 0);
+ if (!l3_node_0) {
+ pr_err("can't find the L3 cluster 0 dt node\n");
+ return;
+ }
+
+ l3_dev_0 = of_find_device_by_node(l3_node_0);
+ if (!l3_dev_0) {
+ pr_err("can't find the L3 cluster 0 dt device\n");
+ return;
+ }
+
+ if (add_opp(&l3_clk, &l3_dev_0->dev))
+ pr_err("Failed to add OPP levels for L3 cluster 0\n");
+
+ l3_node_4 = of_parse_phandle(pdev->dev.of_node, "l3-dev4", 0);
+ if (!l3_node_4) {
+ pr_err("can't find the L3 cluster 1 dt node\n");
+ return;
+ }
+
+ l3_dev_4 = of_find_device_by_node(l3_node_4);
+ if (!l3_dev_4) {
+ pr_err("can't find the L3 cluster 1 dt device\n");
+ return;
+ }
+
+ if (add_opp(&l3_clk, &l3_dev_4->dev))
+ pr_err("Failed to add OPP levels for L3 cluster 1\n");
}
static u64 clk_osm_get_cpu_cycle_counter(int cpu)
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index ae9d509..a5548e0 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -105,20 +105,6 @@
"core_bi_pll_test_se",
};
-static const struct parent_map gpu_cc_parent_map_2[] = {
- { P_BI_TCXO, 0 },
- { P_GPLL0_OUT_MAIN, 5 },
- { P_GPLL0_OUT_MAIN_DIV, 6 },
- { P_CORE_BI_PLL_TEST_SE, 7 },
-};
-
-static const char * const gpu_cc_parent_names_2[] = {
- "bi_tcxo",
- "gcc_gpu_gpll0_clk_src",
- "gcc_gpu_gpll0_div_clk_src",
- "core_bi_pll_test_se",
-};
-
static struct pll_vco fabia_vco[] = {
{ 250000000, 2000000000, 0 },
{ 125000000, 1000000000, 1 },
@@ -232,29 +218,6 @@
},
};
-static const struct freq_tbl ftbl_gpu_cc_rbcpr_clk_src[] = {
- F(19200000, P_BI_TCXO, 1, 0, 0),
- { }
-};
-
-static struct clk_rcg2 gpu_cc_rbcpr_clk_src = {
- .cmd_rcgr = 0x10b0,
- .mnd_width = 0,
- .hid_width = 5,
- .parent_map = gpu_cc_parent_map_2,
- .freq_tbl = ftbl_gpu_cc_rbcpr_clk_src,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "gpu_cc_rbcpr_clk_src",
- .parent_names = gpu_cc_parent_names_2,
- .num_parents = 4,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_rcg2_ops,
- VDD_CX_FMAX_MAP2(
- MIN, 19200000,
- NOMINAL, 50000000),
- },
-};
-
static struct clk_branch gpu_cc_acd_ahb_clk = {
.halt_reg = 0x1168,
.halt_check = BRANCH_HALT,
@@ -488,37 +451,6 @@
},
};
-static struct clk_branch gpu_cc_rbcpr_ahb_clk = {
- .halt_reg = 0x10f4,
- .halt_check = BRANCH_HALT,
- .clkr = {
- .enable_reg = 0x10f4,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gpu_cc_rbcpr_ahb_clk",
- .ops = &clk_branch2_ops,
- },
- },
-};
-
-static struct clk_branch gpu_cc_rbcpr_clk = {
- .halt_reg = 0x10f0,
- .halt_check = BRANCH_HALT,
- .clkr = {
- .enable_reg = 0x10f0,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gpu_cc_rbcpr_clk",
- .parent_names = (const char *[]){
- "gpu_cc_rbcpr_clk_src",
- },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
- },
- },
-};
-
static struct clk_regmap *gpu_cc_sdm845_clocks[] = {
[GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr,
[GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr,
@@ -536,9 +468,6 @@
[GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr,
[GPU_CC_GX_VSENSE_CLK] = &gpu_cc_gx_vsense_clk.clkr,
[GPU_CC_PLL_TEST_CLK] = &gpu_cc_pll_test_clk.clkr,
- [GPU_CC_RBCPR_AHB_CLK] = &gpu_cc_rbcpr_ahb_clk.clkr,
- [GPU_CC_RBCPR_CLK] = &gpu_cc_rbcpr_clk.clkr,
- [GPU_CC_RBCPR_CLK_SRC] = &gpu_cc_rbcpr_clk_src.clkr,
};
static struct clk_regmap *gpu_cc_gfx_sdm845_clocks[] = {
@@ -554,7 +483,6 @@
[GPUCC_GPU_CC_GFX3D_AON_BCR] = { 0x10a0 },
[GPUCC_GPU_CC_GMU_BCR] = { 0x111c },
[GPUCC_GPU_CC_GX_BCR] = { 0x1008 },
- [GPUCC_GPU_CC_RBCPR_BCR] = { 0x10ac },
[GPUCC_GPU_CC_SPDM_BCR] = { 0x1110 },
[GPUCC_GPU_CC_XO_BCR] = { 0x1000 },
};
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index f184ee1..ff64631 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -583,44 +583,92 @@
static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
bool high_bw_req)
{
+ struct crypto_priv *cp = pengine->pcp;
+ unsigned int control_flag;
int ret = 0;
- if (high_bw_req) {
+ if (cp->ce_support.req_bw_before_clk) {
+ if (high_bw_req)
+ control_flag = QCE_BW_REQUEST_FIRST;
+ else
+ control_flag = QCE_CLK_DISABLE_FIRST;
+ } else {
+ if (high_bw_req)
+ control_flag = QCE_CLK_ENABLE_FIRST;
+ else
+ control_flag = QCE_BW_REQUEST_RESET_FIRST;
+ }
+
+ switch (control_flag) {
+ case QCE_CLK_ENABLE_FIRST:
ret = qce_enable_clk(pengine->qce);
if (ret) {
pr_err("%s Unable enable clk\n", __func__);
- goto clk_err;
+ return;
}
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 1);
if (ret) {
- pr_err("%s Unable to set to high bandwidth\n",
- __func__);
- qce_disable_clk(pengine->qce);
- goto clk_err;
+ pr_err("%s Unable to set high bw\n", __func__);
+ ret = qce_disable_clk(pengine->qce);
+ if (ret)
+ pr_err("%s Unable disable clk\n", __func__);
+ return;
}
- } else {
+ break;
+ case QCE_BW_REQUEST_FIRST:
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 1);
+ if (ret) {
+ pr_err("%s Unable to set high bw\n", __func__);
+ return;
+ }
+ ret = qce_enable_clk(pengine->qce);
+ if (ret) {
+ pr_err("%s Unable enable clk\n", __func__);
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 0);
+ if (ret)
+ pr_err("%s Unable to set low bw\n", __func__);
+ return;
+ }
+ break;
+ case QCE_CLK_DISABLE_FIRST:
+ ret = qce_disable_clk(pengine->qce);
+ if (ret) {
+ pr_err("%s Unable to disable clk\n", __func__);
+ return;
+ }
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 0);
if (ret) {
- pr_err("%s Unable to set to low bandwidth\n",
- __func__);
- goto clk_err;
+ pr_err("%s Unable to set low bw\n", __func__);
+ ret = qce_enable_clk(pengine->qce);
+ if (ret)
+ pr_err("%s Unable enable clk\n", __func__);
+ return;
+ }
+ break;
+ case QCE_BW_REQUEST_RESET_FIRST:
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 0);
+ if (ret) {
+ pr_err("%s Unable to set low bw\n", __func__);
+ return;
}
ret = qce_disable_clk(pengine->qce);
if (ret) {
- pr_err("%s Unable disable clk\n", __func__);
+ pr_err("%s Unable to disable clk\n", __func__);
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 1);
if (ret)
- pr_err("%s Unable to set to high bandwidth\n",
- __func__);
- goto clk_err;
+ pr_err("%s Unable to set high bw\n", __func__);
+ return;
}
+ break;
+ default:
+ return;
}
-clk_err:
- return;
-
}
static void qcrypto_bw_reaper_timer_callback(unsigned long data)
@@ -4856,12 +4904,36 @@
if (!pengine)
return -ENOMEM;
- /* open qce */
+ cp->platform_support.bus_scale_table = (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ if (!cp->platform_support.bus_scale_table) {
+ dev_err(&pdev->dev, "bus_scale_table is NULL\n");
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
+ } else {
+ pengine->bus_scale_handle = msm_bus_scale_register_client(
+ (struct msm_bus_scale_pdata *)
+ cp->platform_support.bus_scale_table);
+ if (!pengine->bus_scale_handle) {
+ dev_err(&pdev->dev, "failed to get bus scale handle\n");
+ rc = -ENOMEM;
+ goto exit_kzfree;
+ }
+ pengine->bw_state = BUS_NO_BANDWIDTH;
+ }
+ rc = msm_bus_scale_client_update_request(pengine->bus_scale_handle, 1);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to set high bandwidth\n");
+ goto exit_kzfree;
+ }
handle = qce_open(pdev, &rc);
if (handle == NULL) {
- kzfree(pengine);
- platform_set_drvdata(pdev, NULL);
- return rc;
+ rc = -ENODEV;
+ goto exit_free_pdata;
+ }
+ rc = msm_bus_scale_client_update_request(pengine->bus_scale_handle, 0);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to set low bandwidth\n");
+ goto exit_qce_close;
}
platform_set_drvdata(pdev, pengine);
@@ -4903,7 +4975,7 @@
pengine->max_req, GFP_KERNEL);
if (pqcrypto_req_control == NULL) {
rc = -ENOMEM;
- goto err;
+ goto exit_unlock_mutex;
}
qcrypto_init_req_control(pengine, pqcrypto_req_control);
if (cp->ce_support.bam) {
@@ -4911,15 +4983,7 @@
cp->platform_support.shared_ce_resource = 0;
cp->platform_support.hw_key_support = cp->ce_support.hw_key;
cp->platform_support.sha_hmac = 1;
-
- cp->platform_support.bus_scale_table =
- (struct msm_bus_scale_pdata *)
- msm_bus_cl_get_pdata(pdev);
- if (!cp->platform_support.bus_scale_table)
- pr_warn("bus_scale_table is NULL\n");
-
pengine->ce_device = cp->ce_support.ce_device;
-
} else {
platform_support =
(struct msm_ce_hw_support *)pdev->dev.platform_data;
@@ -4928,33 +4992,11 @@
platform_support->shared_ce_resource;
cp->platform_support.hw_key_support =
platform_support->hw_key_support;
- cp->platform_support.bus_scale_table =
- platform_support->bus_scale_table;
cp->platform_support.sha_hmac = platform_support->sha_hmac;
}
- pengine->bus_scale_handle = 0;
-
- if (cp->platform_support.bus_scale_table != NULL) {
- pengine->bus_scale_handle =
- msm_bus_scale_register_client(
- (struct msm_bus_scale_pdata *)
- cp->platform_support.bus_scale_table);
- if (!pengine->bus_scale_handle) {
- pr_err("%s not able to get bus scale\n",
- __func__);
- rc = -ENOMEM;
- goto err;
- }
- pengine->bw_state = BUS_NO_BANDWIDTH;
- } else {
- pengine->bw_state = BUS_HAS_BANDWIDTH;
- }
-
- if (cp->total_units != 1) {
- mutex_unlock(&cp->engine_lock);
- return 0;
- }
+ if (cp->total_units != 1)
+ goto exit_unlock_mutex;
/* register crypto cipher algorithms the device supports */
for (i = 0; i < ARRAY_SIZE(_qcrypto_ablk_cipher_algos); i++) {
@@ -5243,13 +5285,19 @@
}
mutex_unlock(&cp->engine_lock);
-
return 0;
err:
_qcrypto_remove_engine(pengine);
+ kzfree(pqcrypto_req_control);
+exit_unlock_mutex:
mutex_unlock(&cp->engine_lock);
+exit_qce_close:
if (pengine->qce)
qce_close(pengine->qce);
+exit_free_pdata:
+ msm_bus_scale_client_update_request(pengine->bus_scale_handle, 0);
+ platform_set_drvdata(pdev, NULL);
+exit_kzfree:
kzfree(pengine);
return rc;
};
diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h
index a0e52a0..8c533ee 100644
--- a/drivers/devfreq/governor_memlat.h
+++ b/drivers/devfreq/governor_memlat.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-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
@@ -11,8 +11,8 @@
* GNU General Public License for more details.
*/
-#ifndef _GOVERNOR_BW_HWMON_H
-#define _GOVERNOR_BW_HWMON_H
+#ifndef _GOVERNOR_MEMLAT_H
+#define _GOVERNOR_MEMLAT_H
#include <linux/kernel.h>
#include <linux/devfreq.h>
diff --git a/drivers/dma-buf/fence.c b/drivers/dma-buf/fence.c
index 2453e07..094548b 100644
--- a/drivers/dma-buf/fence.c
+++ b/drivers/dma-buf/fence.c
@@ -68,6 +68,8 @@
struct fence_cb *cur, *tmp;
int ret = 0;
+ lockdep_assert_held(fence->lock);
+
if (WARN_ON(!fence))
return -EINVAL;
@@ -159,9 +161,6 @@
if (WARN_ON(timeout < 0))
return -EINVAL;
- if (timeout == 0)
- return fence_is_signaled(fence);
-
trace_fence_wait_start(fence);
ret = fence->ops->wait(fence, intr, timeout);
trace_fence_wait_end(fence);
diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c
index 723d8af..82f35a4 100644
--- a/drivers/dma-buf/reservation.c
+++ b/drivers/dma-buf/reservation.c
@@ -280,18 +280,24 @@
unsigned *pshared_count,
struct fence ***pshared)
{
- unsigned shared_count = 0;
- unsigned retry = 1;
- struct fence **shared = NULL, *fence_excl = NULL;
- int ret = 0;
+ struct fence **shared = NULL;
+ struct fence *fence_excl;
+ unsigned int shared_count;
+ int ret = 1;
- while (retry) {
+ do {
struct reservation_object_list *fobj;
unsigned seq;
+ unsigned int i;
- seq = read_seqcount_begin(&obj->seq);
+ shared_count = i = 0;
rcu_read_lock();
+ seq = read_seqcount_begin(&obj->seq);
+
+ fence_excl = rcu_dereference(obj->fence_excl);
+ if (fence_excl && !fence_get_rcu(fence_excl))
+ goto unlock;
fobj = rcu_dereference(obj->fence);
if (fobj) {
@@ -309,52 +315,37 @@
}
ret = -ENOMEM;
- shared_count = 0;
break;
}
shared = nshared;
- memcpy(shared, fobj->shared, sz);
shared_count = fobj->shared_count;
- } else
- shared_count = 0;
- fence_excl = rcu_dereference(obj->fence_excl);
-
- retry = read_seqcount_retry(&obj->seq, seq);
- if (retry)
- goto unlock;
-
- if (!fence_excl || fence_get_rcu(fence_excl)) {
- unsigned i;
for (i = 0; i < shared_count; ++i) {
- if (fence_get_rcu(shared[i]))
- continue;
-
- /* uh oh, refcount failed, abort and retry */
- while (i--)
- fence_put(shared[i]);
-
- if (fence_excl) {
- fence_put(fence_excl);
- fence_excl = NULL;
- }
-
- retry = 1;
- break;
+ shared[i] = rcu_dereference(fobj->shared[i]);
+ if (!fence_get_rcu(shared[i]))
+ break;
}
- } else
- retry = 1;
+ }
+ if (i != shared_count || read_seqcount_retry(&obj->seq, seq)) {
+ while (i--)
+ fence_put(shared[i]);
+ fence_put(fence_excl);
+ goto unlock;
+ }
+
+ ret = 0;
unlock:
rcu_read_unlock();
- }
- *pshared_count = shared_count;
- if (shared_count)
- *pshared = shared;
- else {
- *pshared = NULL;
+ } while (ret);
+
+ if (!shared_count) {
kfree(shared);
+ shared = NULL;
}
+
+ *pshared_count = shared_count;
+ *pshared = shared;
*pfence_excl = fence_excl;
return ret;
@@ -379,10 +370,7 @@
{
struct fence *fence;
unsigned seq, shared_count, i = 0;
- long ret = timeout;
-
- if (!timeout)
- return reservation_object_test_signaled_rcu(obj, wait_all);
+ long ret = timeout ? timeout : 1;
retry:
fence = NULL;
@@ -397,9 +385,6 @@
if (fobj)
shared_count = fobj->shared_count;
- if (read_seqcount_retry(&obj->seq, seq))
- goto unlock_retry;
-
for (i = 0; i < shared_count; ++i) {
struct fence *lfence = rcu_dereference(fobj->shared[i]);
@@ -422,9 +407,6 @@
if (!shared_count) {
struct fence *fence_excl = rcu_dereference(obj->fence_excl);
- if (read_seqcount_retry(&obj->seq, seq))
- goto unlock_retry;
-
if (fence_excl &&
!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence_excl->flags)) {
if (!fence_get_rcu(fence_excl))
@@ -439,6 +421,11 @@
rcu_read_unlock();
if (fence) {
+ if (read_seqcount_retry(&obj->seq, seq)) {
+ fence_put(fence);
+ goto retry;
+ }
+
ret = fence_wait_timeout(fence, intr, ret);
fence_put(fence);
if (ret > 0 && wait_all && (i + 1 < shared_count))
@@ -484,12 +471,13 @@
bool test_all)
{
unsigned seq, shared_count;
- int ret = true;
+ int ret;
+ rcu_read_lock();
retry:
+ ret = true;
shared_count = 0;
seq = read_seqcount_begin(&obj->seq);
- rcu_read_lock();
if (test_all) {
unsigned i;
@@ -500,46 +488,35 @@
if (fobj)
shared_count = fobj->shared_count;
- if (read_seqcount_retry(&obj->seq, seq))
- goto unlock_retry;
-
for (i = 0; i < shared_count; ++i) {
struct fence *fence = rcu_dereference(fobj->shared[i]);
ret = reservation_object_test_signaled_single(fence);
if (ret < 0)
- goto unlock_retry;
+ goto retry;
else if (!ret)
break;
}
- /*
- * There could be a read_seqcount_retry here, but nothing cares
- * about whether it's the old or newer fence pointers that are
- * signaled. That race could still have happened after checking
- * read_seqcount_retry. If you care, use ww_mutex_lock.
- */
+ if (read_seqcount_retry(&obj->seq, seq))
+ goto retry;
}
if (!shared_count) {
struct fence *fence_excl = rcu_dereference(obj->fence_excl);
- if (read_seqcount_retry(&obj->seq, seq))
- goto unlock_retry;
-
if (fence_excl) {
ret = reservation_object_test_signaled_single(
fence_excl);
if (ret < 0)
- goto unlock_retry;
+ goto retry;
+
+ if (read_seqcount_retry(&obj->seq, seq))
+ goto retry;
}
}
rcu_read_unlock();
return ret;
-
-unlock_retry:
- rcu_read_unlock();
- goto retry;
}
EXPORT_SYMBOL_GPL(reservation_object_test_signaled_rcu);
diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c
index 454d3b3..0cb8d9d 100644
--- a/drivers/dma-buf/sw_sync.c
+++ b/drivers/dma-buf/sw_sync.c
@@ -324,8 +324,8 @@
}
sync_file = sync_file_create(&pt->base);
+ fence_put(&pt->base);
if (!sync_file) {
- fence_put(&pt->base);
err = -ENOMEM;
goto err;
}
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index 8a9cf92..5a9166a 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -67,9 +67,10 @@
* sync_file_create() - creates a sync file
* @fence: fence to add to the sync_fence
*
- * Creates a sync_file containg @fence. Once this is called, the sync_file
- * takes ownership of @fence. The sync_file can be released with
- * fput(sync_file->file). Returns the sync_file or NULL in case of error.
+ * Creates a sync_file containg @fence. This function acquires and additional
+ * reference of @fence for the newly-created &sync_file, if it succeeds. The
+ * sync_file can be released with fput(sync_file->file). Returns the
+ * sync_file or NULL in case of error.
*/
struct sync_file *sync_file_create(struct fence *fence)
{
@@ -79,7 +80,7 @@
if (!sync_file)
return NULL;
- sync_file->fence = fence;
+ sync_file->fence = fence_get(fence);
snprintf(sync_file->name, sizeof(sync_file->name), "%s-%s%llu-%d",
fence->ops->get_driver_name(fence),
@@ -90,13 +91,6 @@
}
EXPORT_SYMBOL(sync_file_create);
-/**
- * sync_file_fdget() - get a sync_file from an fd
- * @fd: fd referencing a fence
- *
- * Ensures @fd references a valid sync_file, increments the refcount of the
- * backing file. Returns the sync_file or NULL in case of error.
- */
static struct sync_file *sync_file_fdget(int fd)
{
struct file *file = fget(fd);
@@ -311,10 +305,9 @@
poll_wait(file, &sync_file->wq, wait);
- if (!poll_does_not_wait(wait) &&
- !test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) {
+ if (!test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) {
if (fence_add_callback(sync_file->fence, &sync_file->cb,
- fence_check_cb_func) < 0)
+ fence_check_cb_func) < 0)
wake_up_all(&sync_file->wq);
}
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index b98ede7..6f9c9ac 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -208,9 +208,11 @@
* OMAP's debounce time is in 31us steps
* <debounce time> = (GPIO_DEBOUNCINGTIME[7:0].DEBOUNCETIME + 1) x 31
* so we need to convert and round up to the closest unit.
+ *
+ * Return: 0 on success, negative error otherwise.
*/
-static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
- unsigned debounce)
+static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
+ unsigned debounce)
{
void __iomem *reg;
u32 val;
@@ -218,11 +220,12 @@
bool enable = !!debounce;
if (!bank->dbck_flag)
- return;
+ return -ENOTSUPP;
if (enable) {
debounce = DIV_ROUND_UP(debounce, 31) - 1;
- debounce &= OMAP4_GPIO_DEBOUNCINGTIME_MASK;
+ if ((debounce & OMAP4_GPIO_DEBOUNCINGTIME_MASK) != debounce)
+ return -EINVAL;
}
l = BIT(offset);
@@ -255,6 +258,8 @@
bank->context.debounce = debounce;
bank->context.debounce_en = val;
}
+
+ return 0;
}
/**
@@ -964,14 +969,20 @@
{
struct gpio_bank *bank;
unsigned long flags;
+ int ret;
bank = gpiochip_get_data(chip);
raw_spin_lock_irqsave(&bank->lock, flags);
- omap2_set_gpio_debounce(bank, offset, debounce);
+ ret = omap2_set_gpio_debounce(bank, offset, debounce);
raw_spin_unlock_irqrestore(&bank->lock, flags);
- return 0;
+ if (ret)
+ dev_info(chip->parent,
+ "Could not set line %u debounce to %u microseconds (%d)",
+ offset, debounce, ret);
+
+ return ret;
}
static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 6898aa0..2f936a7 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -13,6 +13,7 @@
select I2C
select I2C_ALGOBIT
select DMA_SHARED_BUFFER
+ select SYNC_FILE
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 882404c..42448c7 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -1173,23 +1173,10 @@
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1297,14 +1284,14 @@
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce10_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1319,7 +1306,7 @@
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1358,7 +1345,7 @@
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 64a1df6..904dabd 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -1140,23 +1140,10 @@
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1264,14 +1251,14 @@
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce10_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1286,7 +1273,7 @@
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1325,7 +1312,7 @@
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
index fde6ee1..6d02bdb 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
@@ -932,23 +932,10 @@
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1057,18 +1044,18 @@
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce6_wm_params wm_low, wm_high;
u32 dram_channels;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 priority_a_mark = 0, priority_b_mark = 0;
u32 priority_a_cnt = PRIORITY_OFF;
u32 priority_b_cnt = PRIORITY_OFF;
- u32 tmp, arb_control3;
+ u32 tmp, arb_control3, lb_vblank_lead_lines = 0;
fixed20_12 a, b, c;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
priority_a_cnt = 0;
priority_b_cnt = 0;
@@ -1087,7 +1074,7 @@
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1114,7 +1101,7 @@
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1175,6 +1162,8 @@
c.full = dfixed_div(c, a);
priority_b_mark = dfixed_trunc(c);
priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK;
+
+ lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -1204,6 +1193,9 @@
/* save values for DPM */
amdgpu_crtc->line_time = line_time;
amdgpu_crtc->wm_high = latency_watermark_a;
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines;
}
/* watermark setup */
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 7d9ffde..b1fb601 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -1094,23 +1094,10 @@
a.full = dfixed_const(available_bandwidth);
b.full = dfixed_const(wm->num_heads);
a.full = dfixed_div(a, b);
+ tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512);
+ tmp = min(dfixed_trunc(a), tmp);
- b.full = dfixed_const(mc_latency + 512);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(b, c);
-
- c.full = dfixed_const(dmif_size);
- b.full = dfixed_div(c, b);
-
- tmp = min(dfixed_trunc(a), dfixed_trunc(b));
-
- b.full = dfixed_const(1000);
- c.full = dfixed_const(wm->disp_clk);
- b.full = dfixed_div(c, b);
- c.full = dfixed_const(wm->bytes_per_pixel);
- b.full = dfixed_mul(b, c);
-
- lb_fill_bw = min(tmp, dfixed_trunc(b));
+ lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000);
a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
b.full = dfixed_const(1000);
@@ -1218,14 +1205,14 @@
{
struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
struct dce8_wm_params wm_low, wm_high;
- u32 pixel_period;
+ u32 active_time;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
- pixel_period = 1000000 / (u32)mode->clock;
- line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock;
+ line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535);
/* watermark for high clocks */
if (adev->pm.dpm_enabled) {
@@ -1240,7 +1227,7 @@
wm_high.disp_clk = mode->clock;
wm_high.src_width = mode->crtc_hdisplay;
- wm_high.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_high.active_time = active_time;
wm_high.blank_time = line_time - wm_high.active_time;
wm_high.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -1279,7 +1266,7 @@
wm_low.disp_clk = mode->clock;
wm_low.src_width = mode->crtc_hdisplay;
- wm_low.active_time = mode->crtc_hdisplay * pixel_period;
+ wm_low.active_time = active_time;
wm_low.blank_time = line_time - wm_low.active_time;
wm_low.interlaced = false;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 4e19bde..99011621 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -30,6 +30,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_mode.h>
#include <drm/drm_plane_helper.h>
+#include <linux/sync_file.h>
#include "drm_crtc_internal.h"
@@ -292,6 +293,23 @@
}
EXPORT_SYMBOL(drm_atomic_get_crtc_state);
+static void set_out_fence_for_crtc(struct drm_atomic_state *state,
+ struct drm_crtc *crtc, s32 __user *fence_ptr)
+{
+ state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = fence_ptr;
+}
+
+static s32 __user *get_out_fence_for_crtc(struct drm_atomic_state *state,
+ struct drm_crtc *crtc)
+{
+ s32 __user *fence_ptr;
+
+ fence_ptr = state->crtcs[drm_crtc_index(crtc)].out_fence_ptr;
+ state->crtcs[drm_crtc_index(crtc)].out_fence_ptr = NULL;
+
+ return fence_ptr;
+}
+
/**
* drm_atomic_set_mode_for_crtc - set mode for CRTC
* @state: the CRTC whose incoming state to update
@@ -496,6 +514,16 @@
&replaced);
state->color_mgmt_changed |= replaced;
return ret;
+ } else if (property == config->prop_out_fence_ptr) {
+ s32 __user *fence_ptr = u64_to_user_ptr(val);
+
+ if (!fence_ptr)
+ return 0;
+
+ if (put_user(-1, fence_ptr))
+ return -EFAULT;
+
+ set_out_fence_for_crtc(state->state, crtc, fence_ptr);
} else if (crtc->funcs->atomic_set_property)
return crtc->funcs->atomic_set_property(crtc, state, property, val);
else
@@ -538,6 +566,8 @@
*val = (state->ctm) ? state->ctm->base.id : 0;
else if (property == config->gamma_lut_property)
*val = (state->gamma_lut) ? state->gamma_lut->base.id : 0;
+ else if (property == config->prop_out_fence_ptr)
+ *val = 0;
else if (crtc->funcs->atomic_get_property)
return crtc->funcs->atomic_get_property(crtc, state, property, val);
else
@@ -693,6 +723,17 @@
drm_atomic_set_fb_for_plane(state, fb);
if (fb)
drm_framebuffer_unreference(fb);
+ } else if (property == config->prop_in_fence_fd) {
+ if (state->fence)
+ return -EINVAL;
+
+ if (U642I64(val) == -1)
+ return 0;
+
+ state->fence = sync_file_get_fence(val);
+ if (!state->fence)
+ return -EINVAL;
+
} else if (property == config->prop_crtc_id) {
struct drm_crtc *crtc = drm_crtc_find(dev, val);
return drm_atomic_set_crtc_for_plane(state, crtc);
@@ -752,6 +793,8 @@
if (property == config->prop_fb_id) {
*val = (state->fb) ? state->fb->base.id : 0;
+ } else if (property == config->prop_in_fence_fd) {
+ *val = -1;
} else if (property == config->prop_crtc_id) {
*val = (state->crtc) ? state->crtc->base.id : 0;
} else if (property == config->prop_crtc_x) {
@@ -1152,6 +1195,36 @@
EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
/**
+ * drm_atomic_set_fence_for_plane - set fence for plane
+ * @plane_state: atomic state object for the plane
+ * @fence: fence to use for the plane
+ *
+ * Helper to setup the plane_state fence in case it is not set yet.
+ * By using this drivers doesn't need to worry if the user choose
+ * implicit or explicit fencing.
+ *
+ * This function will not set the fence to the state if it was set
+ * via explicit fencing interfaces on the atomic ioctl. It will
+ * all drope the reference to the fence as we not storing it
+ * anywhere.
+ *
+ * Otherwise, if plane_state->fence is not set this function we
+ * just set it with the received implict fence.
+ */
+void
+drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state,
+ struct fence *fence)
+{
+ if (plane_state->fence) {
+ fence_put(fence);
+ return;
+ }
+
+ plane_state->fence = fence;
+}
+EXPORT_SYMBOL(drm_atomic_set_fence_for_plane);
+
+/**
* drm_atomic_set_crtc_for_connector - set crtc for connector
* @conn_state: atomic state object for the connector
* @crtc: crtc to use for the connector
@@ -1467,11 +1540,9 @@
*/
static struct drm_pending_vblank_event *create_vblank_event(
- struct drm_device *dev, struct drm_file *file_priv,
- struct fence *fence, uint64_t user_data)
+ struct drm_device *dev, uint64_t user_data)
{
struct drm_pending_vblank_event *e = NULL;
- int ret;
e = kzalloc(sizeof *e, GFP_KERNEL);
if (!e)
@@ -1481,17 +1552,6 @@
e->event.base.length = sizeof(e->event);
e->event.user_data = user_data;
- if (file_priv) {
- ret = drm_event_reserve_init(dev, file_priv, &e->base,
- &e->event.base);
- if (ret) {
- kfree(e);
- return NULL;
- }
- }
-
- e->base.fence = fence;
-
return e;
}
@@ -1596,6 +1656,206 @@
}
EXPORT_SYMBOL(drm_atomic_clean_old_fb);
+/**
+ * DOC: explicit fencing properties
+ *
+ * Explicit fencing allows userspace to control the buffer synchronization
+ * between devices. A Fence or a group of fences are transfered to/from
+ * userspace using Sync File fds and there are two DRM properties for that.
+ * IN_FENCE_FD on each DRM Plane to send fences to the kernel and
+ * OUT_FENCE_PTR on each DRM CRTC to receive fences from the kernel.
+ *
+ * As a contrast, with implicit fencing the kernel keeps track of any
+ * ongoing rendering, and automatically ensures that the atomic update waits
+ * for any pending rendering to complete. For shared buffers represented with
+ * a struct &dma_buf this is tracked in &reservation_object structures.
+ * Implicit syncing is how Linux traditionally worked (e.g. DRI2/3 on X.org),
+ * whereas explicit fencing is what Android wants.
+ *
+ * "IN_FENCE_FD”:
+ * Use this property to pass a fence that DRM should wait on before
+ * proceeding with the Atomic Commit request and show the framebuffer for
+ * the plane on the screen. The fence can be either a normal fence or a
+ * merged one, the sync_file framework will handle both cases and use a
+ * fence_array if a merged fence is received. Passing -1 here means no
+ * fences to wait on.
+ *
+ * If the Atomic Commit request has the DRM_MODE_ATOMIC_TEST_ONLY flag
+ * it will only check if the Sync File is a valid one.
+ *
+ * On the driver side the fence is stored on the @fence parameter of
+ * struct &drm_plane_state. Drivers which also support implicit fencing
+ * should set the implicit fence using drm_atomic_set_fence_for_plane(),
+ * to make sure there's consistent behaviour between drivers in precedence
+ * of implicit vs. explicit fencing.
+ *
+ * "OUT_FENCE_PTR”:
+ * Use this property to pass a file descriptor pointer to DRM. Once the
+ * Atomic Commit request call returns OUT_FENCE_PTR will be filled with
+ * the file descriptor number of a Sync File. This Sync File contains the
+ * CRTC fence that will be signaled when all framebuffers present on the
+ * Atomic Commit * request for that given CRTC are scanned out on the
+ * screen.
+ *
+ * The Atomic Commit request fails if a invalid pointer is passed. If the
+ * Atomic Commit request fails for any other reason the out fence fd
+ * returned will be -1. On a Atomic Commit with the
+ * DRM_MODE_ATOMIC_TEST_ONLY flag the out fence will also be set to -1.
+ *
+ * Note that out-fences don't have a special interface to drivers and are
+ * internally represented by a struct &drm_pending_vblank_event in struct
+ * &drm_crtc_state, which is also used by the nonblocking atomic commit
+ * helpers and for the DRM event handling for existing userspace.
+ */
+
+struct drm_out_fence_state {
+ s32 __user *out_fence_ptr;
+ struct sync_file *sync_file;
+ int fd;
+};
+
+static int setup_out_fence(struct drm_out_fence_state *fence_state,
+ struct fence *fence)
+{
+ fence_state->fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fence_state->fd < 0)
+ return fence_state->fd;
+
+ if (put_user(fence_state->fd, fence_state->out_fence_ptr))
+ return -EFAULT;
+
+ fence_state->sync_file = sync_file_create(fence);
+ if (!fence_state->sync_file)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int prepare_crtc_signaling(struct drm_device *dev,
+ struct drm_atomic_state *state,
+ struct drm_mode_atomic *arg,
+ struct drm_file *file_priv,
+ struct drm_out_fence_state **fence_state,
+ unsigned int *num_fences)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ int i, ret;
+
+ if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
+ return 0;
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ s32 __user *fence_ptr;
+
+ fence_ptr = get_out_fence_for_crtc(crtc_state->state, crtc);
+
+ if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) {
+ struct drm_pending_vblank_event *e;
+
+ e = create_vblank_event(dev, arg->user_data);
+ if (!e)
+ return -ENOMEM;
+
+ crtc_state->event = e;
+ }
+
+ if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+ struct drm_pending_vblank_event *e = crtc_state->event;
+
+ if (!file_priv)
+ continue;
+
+ ret = drm_event_reserve_init(dev, file_priv, &e->base,
+ &e->event.base);
+ if (ret) {
+ kfree(e);
+ crtc_state->event = NULL;
+ return ret;
+ }
+ }
+
+ if (fence_ptr) {
+ struct fence *fence;
+ struct drm_out_fence_state *f;
+
+ f = krealloc(*fence_state, sizeof(**fence_state) *
+ (*num_fences + 1), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ memset(&f[*num_fences], 0, sizeof(*f));
+
+ f[*num_fences].out_fence_ptr = fence_ptr;
+ *fence_state = f;
+
+ fence = drm_crtc_create_fence(crtc);
+ if (!fence)
+ return -ENOMEM;
+
+ ret = setup_out_fence(&f[(*num_fences)++], fence);
+ if (ret) {
+ fence_put(fence);
+ return ret;
+ }
+
+ crtc_state->event->base.fence = fence;
+ }
+ }
+
+ return 0;
+}
+
+static void complete_crtc_signaling(struct drm_device *dev,
+ struct drm_atomic_state *state,
+ struct drm_out_fence_state *fence_state,
+ unsigned int num_fences,
+ bool install_fds)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ int i;
+
+ if (install_fds) {
+ for (i = 0; i < num_fences; i++)
+ fd_install(fence_state[i].fd,
+ fence_state[i].sync_file->file);
+
+ kfree(fence_state);
+ return;
+ }
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ struct drm_pending_vblank_event *event = crtc_state->event;
+ /*
+ * Free the allocated event. drm_atomic_helper_setup_commit
+ * can allocate an event too, so only free it if it's ours
+ * to prevent a double free in drm_atomic_state_clear.
+ */
+ if (event && (event->base.fence || event->base.file_priv)) {
+ drm_event_cancel_free(dev, &event->base);
+ crtc_state->event = NULL;
+ }
+ }
+
+ if (!fence_state)
+ return;
+
+ for (i = 0; i < num_fences; i++) {
+ if (fence_state[i].sync_file)
+ fput(fence_state[i].sync_file->file);
+ if (fence_state[i].fd >= 0)
+ put_unused_fd(fence_state[i].fd);
+
+ /* If this fails log error to the user */
+ if (fence_state[i].out_fence_ptr &&
+ put_user(-1, fence_state[i].out_fence_ptr))
+ DRM_DEBUG_ATOMIC("Couldn't clear out_fence_ptr\n");
+ }
+
+ kfree(fence_state);
+}
+
int drm_mode_atomic_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -1608,11 +1868,10 @@
struct drm_atomic_state *state;
struct drm_modeset_acquire_ctx ctx;
struct drm_plane *plane;
- struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state;
+ struct drm_out_fence_state *fence_state = NULL;
unsigned plane_mask;
int ret = 0;
- unsigned int i, j;
+ unsigned int i, j, num_fences = 0;
/* disallow for drivers not supporting atomic: */
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
@@ -1727,20 +1986,10 @@
drm_mode_object_unreference(obj);
}
- if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- for_each_crtc_in_state(state, crtc, crtc_state, i) {
- struct drm_pending_vblank_event *e;
-
- e = create_vblank_event(dev, file_priv, NULL,
- arg->user_data);
- if (!e) {
- ret = -ENOMEM;
- goto out;
- }
-
- crtc_state->event = e;
- }
- }
+ ret = prepare_crtc_signaling(dev, state, arg, file_priv, &fence_state,
+ &num_fences);
+ if (ret)
+ goto out;
if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
/*
@@ -1757,20 +2006,7 @@
out:
drm_atomic_clean_old_fb(dev, plane_mask, ret);
- if (ret && arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- /*
- * Free the allocated event. drm_atomic_helper_setup_commit
- * can allocate an event too, so only free it if it's ours
- * to prevent a double free in drm_atomic_state_clear.
- */
- for_each_crtc_in_state(state, crtc, crtc_state, i) {
- struct drm_pending_vblank_event *event = crtc_state->event;
- if (event && (event->base.fence || event->base.file_priv)) {
- drm_event_cancel_free(dev, &event->base);
- crtc_state->event = NULL;
- }
- }
- }
+ complete_crtc_signaling(dev, state, fence_state, num_fences, !ret);
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 50acd79..f34b4e8 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -3166,6 +3166,9 @@
{
if (state->fb)
drm_framebuffer_unreference(state->fb);
+
+ if (state->fence)
+ fence_put(state->fence);
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 2d7bedf..79b3d52 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -33,6 +33,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/fence.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
@@ -141,6 +142,54 @@
}
}
+static const struct fence_ops drm_crtc_fence_ops;
+
+static struct drm_crtc *fence_to_crtc(struct fence *fence)
+{
+ BUG_ON(fence->ops != &drm_crtc_fence_ops);
+ return container_of(fence->lock, struct drm_crtc, fence_lock);
+}
+
+static const char *drm_crtc_fence_get_driver_name(struct fence *fence)
+{
+ struct drm_crtc *crtc = fence_to_crtc(fence);
+
+ return crtc->dev->driver->name;
+}
+
+static const char *drm_crtc_fence_get_timeline_name(struct fence *fence)
+{
+ struct drm_crtc *crtc = fence_to_crtc(fence);
+
+ return crtc->timeline_name;
+}
+
+static bool drm_crtc_fence_enable_signaling(struct fence *fence)
+{
+ return true;
+}
+
+static const struct fence_ops drm_crtc_fence_ops = {
+ .get_driver_name = drm_crtc_fence_get_driver_name,
+ .get_timeline_name = drm_crtc_fence_get_timeline_name,
+ .enable_signaling = drm_crtc_fence_enable_signaling,
+ .wait = fence_default_wait,
+};
+
+struct fence *drm_crtc_create_fence(struct drm_crtc *crtc)
+{
+ struct fence *fence;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return NULL;
+
+ fence_init(fence, &drm_crtc_fence_ops, &crtc->fence_lock,
+ crtc->fence_context, ++crtc->fence_seqno);
+
+ return fence;
+}
+
/**
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
@@ -198,6 +247,11 @@
return -ENOMEM;
}
+ crtc->fence_context = fence_context_alloc(1);
+ spin_lock_init(&crtc->fence_lock);
+ snprintf(crtc->timeline_name, sizeof(crtc->timeline_name),
+ "CRTC:%d-%s", crtc->base.id, crtc->name);
+
crtc->base.properties = &crtc->properties;
list_add_tail(&crtc->head, &config->crtc_list);
@@ -213,6 +267,8 @@
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&crtc->base, config->prop_active, 0);
drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
+ drm_object_attach_property(&crtc->base,
+ config->prop_out_fence_ptr, 0);
}
return 0;
@@ -365,6 +421,18 @@
return -ENOMEM;
dev->mode_config.prop_fb_id = prop;
+ prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+ "IN_FENCE_FD", -1, INT_MAX);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_in_fence_fd = prop;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "OUT_FENCE_PTR", 0, U64_MAX);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_out_fence_ptr = prop;
+
prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
"CRTC_ID", DRM_MODE_OBJECT_CRTC);
if (!prop)
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index c48ba02..df2b51a 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -41,6 +41,8 @@
const struct drm_display_mode *mode,
const struct drm_framebuffer *fb);
+struct fence *drm_crtc_create_fence(struct drm_crtc *crtc);
+
void drm_fb_release(struct drm_file *file_priv);
/* dumb buffer support IOCTLs */
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 0bc0afb..4e5ba7e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -76,6 +76,8 @@
#define EDID_QUIRK_FORCE_12BPC (1 << 9)
/* Force 6bpc */
#define EDID_QUIRK_FORCE_6BPC (1 << 10)
+/* Force 10bpc */
+#define EDID_QUIRK_FORCE_10BPC (1 << 11)
struct detailed_mode_closure {
struct drm_connector *connector;
@@ -90,6 +92,14 @@
#define LEVEL_GTF2 2
#define LEVEL_CVT 3
+/*Enum storing luminance types for HDR blocks in EDID*/
+enum luminance_value {
+ NO_LUMINANCE_DATA = 3,
+ MAXIMUM_LUMINANCE = 4,
+ FRAME_AVERAGE_LUMINANCE = 5,
+ MINIMUM_LUMINANCE = 6
+};
+
static const struct edid_quirk {
char vendor[4];
int product_id;
@@ -118,6 +128,9 @@
{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
EDID_QUIRK_DETAILED_IN_CM },
+ /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */
+ { "LGD", 764, EDID_QUIRK_FORCE_10BPC },
+
/* LG Philips LCD LP154W01-A5 */
{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
@@ -997,6 +1010,221 @@
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 65 - 1280x720@24Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 66 - 1280x720@25Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+ 3740, 3960, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 67 - 1280x720@30Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 68 - 1280x720@50Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 69 - 1280x720@60Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 70 - 1280x720@100Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 71 - 1280x720@120Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 72 - 1920x1080@24Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 73 - 1920x1080@25Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 74 - 1920x1080@30Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 75 - 1920x1080@50Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 76 - 1920x1080@60Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 77 - 1920x1080@100Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 78 - 1920x1080@120Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 79 - 1680x720@24Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 80 - 1680x720@25Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
+ 2948, 3168, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 81 - 1680x720@30Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
+ 2420, 2640, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 82 - 1680x720@50Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 83 - 1680x720@60Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 84 - 1680x720@100Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 85 - 1680x720@120Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 86 - 2560x1080@24Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
+ 3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 87 - 2560x1080@25Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
+ 3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 88 - 2560x1080@30Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
+ 3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 89 - 2560x1080@50Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 90 - 2560x1080@60Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
+ 2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 91 - 2560x1080@100Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
+ 2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 92 - 2560x1080@120Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 93 - 3840x2160p@24Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,},
+ /* 94 - 3840x2160p@25Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 95 - 3840x2160p@30Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 96 - 3840x2160p@50Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 97 - 3840x2160p@60Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 98 - 4096x2160p@24Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 99 - 4096x2160p@25Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 100 - 4096x2160p@30Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 101 - 4096x2160p@50Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 102 - 4096x2160p@60Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 103 - 3840x2160p@24Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 104 - 3840x2160p@25Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 105 - 3840x2160p@30Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 106 - 3840x2160p@50Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 107 - 3840x2160p@60Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
};
/*
@@ -2514,12 +2742,15 @@
return closure.modes;
}
-
+#define VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK 0x0
#define AUDIO_BLOCK 0x01
#define VIDEO_BLOCK 0x02
#define VENDOR_BLOCK 0x03
#define SPEAKER_BLOCK 0x04
+#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06
+#define EXTENDED_TAG 0x07
#define VIDEO_CAPABILITY_BLOCK 0x07
+#define Y420_VIDEO_DATA_BLOCK 0x0E
#define EDID_BASIC_AUDIO (1 << 6)
#define EDID_CEA_YCRCB444 (1 << 5)
#define EDID_CEA_YCRCB422 (1 << 4)
@@ -3168,6 +3399,21 @@
return hdmi_id == HDMI_IEEE_OUI;
}
+static bool cea_db_is_hdmi_hf_vsdb(const u8 *db)
+{
+ int hdmi_id;
+
+ if (cea_db_tag(db) != VENDOR_BLOCK)
+ return false;
+
+ if (cea_db_payload_len(db) < 7)
+ return false;
+
+ hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
+
+ return hdmi_id == HDMI_IEEE_OUI_HF;
+}
+
#define for_each_cea_db(cea, i, start, end) \
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
@@ -3287,6 +3533,227 @@
}
static void
+parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
+{
+ u8 len = cea_db_payload_len(db);
+
+ if (len < 7)
+ return;
+
+ if (db[4] != 1)
+ return; /* invalid version */
+
+ connector->max_tmds_char = db[5] * 5;
+ connector->scdc_present = db[6] & (1 << 7);
+ connector->rr_capable = db[6] & (1 << 6);
+ connector->flags_3d = db[6] & 0x7;
+ connector->supports_scramble = connector->scdc_present &&
+ (db[6] & (1 << 3));
+
+ DRM_DEBUG_KMS("HDMI v2: max TMDS char %d, "
+ "scdc %s, "
+ "rr %s, "
+ "3D flags 0x%x, "
+ "scramble %s\n",
+ connector->max_tmds_char,
+ connector->scdc_present ? "available" : "not available",
+ connector->rr_capable ? "capable" : "not capable",
+ connector->flags_3d,
+ connector->supports_scramble ?
+ "supported" : "not supported");
+}
+
+/*
+ * drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the CEA vendor specific block
+ *
+ * Parses the HDMI VCDB to extract sink info for @connector.
+ */
+static void
+drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db)
+{
+ /*
+ * Check if the sink specifies underscan
+ * support for:
+ * BIT 5: preferred video format
+ * BIT 3: IT video format
+ * BIT 1: CE video format
+ */
+
+ connector->pt_scan_info =
+ (db[2] & (BIT(4) | BIT(5))) >> 4;
+ connector->it_scan_info =
+ (db[2] & (BIT(3) | BIT(2))) >> 2;
+ connector->ce_scan_info =
+ db[2] & (BIT(1) | BIT(0));
+
+ DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)",
+ (int) connector->pt_scan_info,
+ (int) connector->it_scan_info,
+ (int) connector->ce_scan_info);
+}
+
+static bool drm_edid_is_luminance_value_present(
+u32 block_length, enum luminance_value value)
+{
+ return block_length > NO_LUMINANCE_DATA && value <= block_length;
+}
+
+/*
+ * drm_extract_hdr_db - Parse the HDMI HDR extended block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the HDMI HDR extended block
+ *
+ * Parses the HDMI HDR extended block to extract sink info for @connector.
+ */
+static void
+drm_extract_hdr_db(struct drm_connector *connector, const u8 *db)
+{
+
+ u8 len = 0;
+
+ if (!db)
+ return;
+
+ len = db[0] & 0x1f;
+ /* Byte 3: Electro-Optical Transfer Functions */
+ connector->hdr_eotf = db[2] & 0x3F;
+
+ /* Byte 4: Static Metadata Descriptor Type 1 */
+ connector->hdr_metadata_type_one = (db[3] & BIT(0));
+
+ /* Byte 5: Desired Content Maximum Luminance */
+ if (drm_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE))
+ connector->hdr_max_luminance =
+ db[MAXIMUM_LUMINANCE];
+
+ /* Byte 6: Desired Content Max Frame-average Luminance */
+ if (drm_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE))
+ connector->hdr_avg_luminance =
+ db[FRAME_AVERAGE_LUMINANCE];
+
+ /* Byte 7: Desired Content Min Luminance */
+ if (drm_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE))
+ connector->hdr_min_luminance =
+ db[MINIMUM_LUMINANCE];
+
+ connector->hdr_supported = true;
+
+ DRM_DEBUG_KMS("HDR electro-optical %d\n", connector->hdr_eotf);
+ DRM_DEBUG_KMS("metadata desc 1 %d\n", connector->hdr_metadata_type_one);
+ DRM_DEBUG_KMS("max luminance %d\n", connector->hdr_max_luminance);
+ DRM_DEBUG_KMS("avg luminance %d\n", connector->hdr_avg_luminance);
+ DRM_DEBUG_KMS("min luminance %d\n", connector->hdr_min_luminance);
+}
+
+/*
+ * drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the all extended tag blocks extract sink info for @connector.
+ */
+static void
+drm_hdmi_extract_extended_blk_info(struct drm_connector *connector,
+struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == EXTENDED_TAG) {
+ DRM_DEBUG_KMS("found extended tag block = %d\n",
+ db[1]);
+ switch (db[1]) {
+ case VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK:
+ drm_extract_vcdb_info(connector, db);
+ break;
+ case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK:
+ drm_extract_hdr_db(connector, db);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+static u8 *
+drm_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid)
+ return NULL;
+
+ cea = drm_find_cea_extension(edid);
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((cea_db_tag(db) == EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * add_YCbCr420VDB_modes - add the modes found in Ycbcr420 VDB block
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the YCbCr420 VDB block and adds the modes to @connector.
+ */
+static int
+add_YCbCr420VDB_modes(struct drm_connector *connector, struct edid *edid)
+{
+
+ const u8 *db = NULL;
+ u32 i = 0;
+ u32 modes = 0;
+ u32 video_format = 0;
+ u8 len = 0;
+
+ /*Find the YCbCr420 VDB*/
+ db = drm_edid_find_extended_tag_block(edid, Y420_VIDEO_DATA_BLOCK);
+ /* Offset to byte 3 */
+ if (db) {
+ len = db[0] & 0x1F;
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ struct drm_display_mode *mode;
+
+ video_format = *(db + i) & 0x7F;
+ mode = drm_display_mode_from_vic_index(connector,
+ db, len-1, i);
+ if (mode) {
+ DRM_DEBUG_KMS("Adding mode for vic = %d\n",
+ video_format);
+ drm_mode_probed_add(connector, mode);
+ modes++;
+ }
+ }
+ }
+ return modes;
+}
+
+static void
monitor_name(struct detailed_timing *t, void *data)
{
if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
@@ -3410,6 +3877,9 @@
/* HDMI Vendor-Specific Data Block */
if (cea_db_is_hdmi_vsdb(db))
drm_parse_hdmi_vsdb_audio(connector, db);
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
break;
default:
break;
@@ -3840,6 +4310,37 @@
}
}
+static void
+drm_hdmi_extract_vsdbs_info(struct drm_connector *connector, struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == VENDOR_BLOCK) {
+ /* HDMI Vendor-Specific Data Block */
+ if (cea_db_is_hdmi_vsdb(db)) {
+ drm_parse_hdmi_vsdb_video(
+ connector, db);
+ drm_parse_hdmi_vsdb_audio(
+ connector, db);
+ }
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
+ }
+ }
+ }
+}
+
static void drm_add_display_info(struct drm_connector *connector,
struct edid *edid)
{
@@ -3877,6 +4378,11 @@
connector->name, info->bpc);
}
+ /* Extract audio and video latency fields for the sink */
+ drm_hdmi_extract_vsdbs_info(connector, edid);
+ /* Extract info from extended tag blocks */
+ drm_hdmi_extract_extended_blk_info(connector, edid);
+
/* Only defined for 1.4 with digital displays */
if (edid->revision < 4)
return;
@@ -4091,6 +4597,7 @@
num_modes += add_cea_modes(connector, edid);
num_modes += add_alternate_cea_modes(connector, edid);
num_modes += add_displayid_detailed_modes(connector, edid);
+ num_modes += add_YCbCr420VDB_modes(connector, edid);
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
num_modes += add_inferred_modes(connector, edid);
@@ -4105,6 +4612,9 @@
if (quirks & EDID_QUIRK_FORCE_8BPC)
connector->display_info.bpc = 8;
+ if (quirks & EDID_QUIRK_FORCE_10BPC)
+ connector->display_info.bpc = 10;
+
if (quirks & EDID_QUIRK_FORCE_12BPC)
connector->display_info.bpc = 12;
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 1fd6eac..52629b6 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -18,13 +18,16 @@
*/
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
+#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
+#include <linux/reservation.h>
#define DEFAULT_FBDEFIO_DELAY_MS 50
@@ -265,6 +268,38 @@
}
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
+/**
+ * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
+ * @plane: Which plane
+ * @state: Plane state attach fence to
+ *
+ * This should be put into prepare_fb hook of struct &drm_plane_helper_funcs .
+ *
+ * This function checks if the plane FB has an dma-buf attached, extracts
+ * the exclusive fence and attaches it to plane state for the atomic helper
+ * to wait on.
+ *
+ * There is no need for cleanup_fb for CMA based framebuffer drivers.
+ */
+int drm_fb_cma_prepare_fb(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct dma_buf *dma_buf;
+ struct fence *fence;
+
+ if ((plane->state->fb == state->fb) || !state->fb)
+ return 0;
+
+ dma_buf = drm_fb_cma_get_gem_obj(state->fb, 0)->base.dma_buf;
+ if (dma_buf) {
+ fence = reservation_object_get_excl_rcu(dma_buf->resv);
+ drm_atomic_set_fence_for_plane(state, fence);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_prepare_fb);
+
#ifdef CONFIG_DEBUG_FS
static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m)
{
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index f5815e1..fe00bea 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -663,6 +663,10 @@
list_del(&p->pending_link);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ if (p->fence)
+ fence_put(p->fence);
+
kfree(p);
}
EXPORT_SYMBOL(drm_event_cancel_free);
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
index 249c0ae..3957ef8 100644
--- a/drivers/gpu/drm/drm_plane.c
+++ b/drivers/gpu/drm/drm_plane.c
@@ -137,6 +137,7 @@
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+ drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index f46aac1..c75f4bb 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -420,6 +420,11 @@
return 0;
}
+ if (intel_vgpu_active(dev_priv)) {
+ DRM_INFO("iGVT-g active, disabling use of stolen memory\n");
+ return 0;
+ }
+
#ifdef CONFIG_INTEL_IOMMU
if (intel_iommu_gfx_mapped && INTEL_INFO(dev)->gen < 8) {
DRM_INFO("DMAR active, disabling use of stolen memory\n");
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4112bef..651c0576 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -51,6 +51,7 @@
sde/sde_hw_reg_dma_v1_color_proc.o \
sde/sde_hw_color_proc_v4.o \
sde/sde_hw_ad4.o \
+ sde_edid_parser.o
msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
sde_rsc_hw.o \
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
new file mode 100644
index 0000000..7102d8a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -0,0 +1,1396 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include "dp_ctrl.h"
+
+#define DP_LINK_RATE_MULTIPLIER 27000000
+#define DP_KHZ_TO_HZ 1000
+#define DP_CRYPTO_CLK_RATE_KHZ 180000
+
+/* sink power state */
+#define SINK_POWER_ON 1
+#define SINK_POWER_OFF 2
+
+#define DP_CTRL_INTR_READY_FOR_VIDEO BIT(0)
+#define DP_CTRL_INTR_IDLE_PATTERN_SENT BIT(3)
+
+/* dp state ctrl */
+#define ST_TRAIN_PATTERN_1 BIT(0)
+#define ST_TRAIN_PATTERN_2 BIT(1)
+#define ST_TRAIN_PATTERN_3 BIT(2)
+#define ST_TRAIN_PATTERN_4 BIT(3)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(4)
+#define ST_PRBS7 BIT(5)
+#define ST_CUSTOM_80_BIT_PATTERN BIT(6)
+#define ST_SEND_VIDEO BIT(7)
+#define ST_PUSH_IDLE BIT(8)
+
+struct dp_vc_tu_mapping_table {
+ u32 vic;
+ u8 lanes;
+ u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
+ u8 bpp;
+ u8 valid_boundary_link;
+ u16 delay_start_link;
+ bool boundary_moderation_en;
+ u8 valid_lower_boundary_link;
+ u8 upper_boundary_count;
+ u8 lower_boundary_count;
+ u8 tu_size_minus1;
+};
+
+struct dp_ctrl_private {
+ struct dp_ctrl dp_ctrl;
+
+ struct device *dev;
+ struct dp_aux *aux;
+ struct dp_panel *panel;
+ struct dp_link *link;
+ struct dp_power *power;
+ struct dp_parser *parser;
+ struct dp_catalog_ctrl *catalog;
+
+ struct completion idle_comp;
+ struct completion video_comp;
+ struct completion irq_comp;
+
+ bool hpd_irq_on;
+ bool power_on;
+ bool sink_info_read;
+ bool cont_splash;
+ bool psm_enabled;
+ bool initialized;
+ bool orientation;
+
+ u32 pixel_rate;
+ u32 vic;
+};
+
+enum notification_status {
+ NOTIFY_UNKNOWN,
+ NOTIFY_CONNECT,
+ NOTIFY_DISCONNECT,
+ NOTIFY_CONNECT_IRQ_HPD,
+ NOTIFY_DISCONNECT_IRQ_HPD,
+};
+
+static void dp_ctrl_idle_patterns_sent(struct dp_ctrl_private *ctrl)
+{
+ pr_debug("idle_patterns_sent\n");
+ complete(&ctrl->idle_comp);
+}
+
+static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl)
+{
+ pr_debug("dp_video_ready\n");
+ complete(&ctrl->video_comp);
+}
+
+static void dp_ctrl_set_sink_power_state(struct dp_ctrl_private *ctrl,
+ u8 power_state)
+{
+ const int len = 1;
+
+ ctrl->aux->write(ctrl->aux, 0x600, len, AUX_NATIVE, &power_state);
+}
+
+static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state)
+{
+ ctrl->catalog->state_ctrl(ctrl->catalog, state);
+}
+
+static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
+{
+ int const idle_pattern_completion_timeout_ms = 3 * HZ / 100;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ dp_ctrl_set_sink_power_state(ctrl, SINK_POWER_OFF);
+
+ reinit_completion(&ctrl->idle_comp);
+ dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
+
+ if (!wait_for_completion_timeout(&ctrl->idle_comp,
+ idle_pattern_completion_timeout_ms))
+ pr_warn("PUSH_IDLE pattern timedout\n");
+
+ pr_debug("mainlink off done\n");
+}
+
+static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
+{
+ u32 config = 0, tbd;
+
+ config |= (2 << 13); /* Default-> LSCLK DIV: 1/4 LCLK */
+ config |= (0 << 11); /* RGB */
+
+ /* Scrambler reset enable */
+ if (ctrl->panel->dpcd.scrambler_reset)
+ config |= (1 << 10);
+
+ tbd = ctrl->link->get_test_bits_depth(ctrl->link,
+ ctrl->panel->pinfo.bpp);
+ config |= tbd << 8;
+
+ /* Num of Lanes */
+ config |= ((ctrl->link->lane_count - 1) << 4);
+
+ if (ctrl->panel->dpcd.enhanced_frame)
+ config |= 0x40;
+
+ config |= 0x04; /* progressive video */
+
+ config |= 0x03; /* sycn clock & static Mvid */
+
+ ctrl->catalog->config_ctrl(ctrl->catalog, config);
+}
+
+/**
+ * dp_ctrl_configure_source_params() - configures DP transmitter source params
+ * @ctrl: Display Port Driver data
+ *
+ * Configures the DP transmitter source params including details such as lane
+ * configuration, output format and sink/panel timing information.
+ */
+static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
+{
+ u32 cc, tb;
+
+ ctrl->catalog->lane_mapping(ctrl->catalog);
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+ dp_ctrl_config_ctrl(ctrl);
+
+ tb = ctrl->link->get_test_bits_depth(ctrl->link,
+ ctrl->panel->pinfo.bpp);
+ cc = ctrl->link->get_colorimetry_config(ctrl->link);
+ ctrl->catalog->config_misc(ctrl->catalog, cc, tb);
+
+ ctrl->catalog->config_msa(ctrl->catalog);
+
+ ctrl->panel->timing_cfg(ctrl->panel);
+}
+
+static void dp_ctrl_get_extra_req_bytes(u64 result_valid,
+ int valid_bdary_link,
+ u64 value1, u64 value2,
+ bool *negative, u64 *result,
+ u64 compare)
+{
+ *negative = false;
+ if (result_valid >= compare) {
+ if (valid_bdary_link
+ >= compare)
+ *result = value1 + value2;
+ else {
+ if (value1 < value2)
+ *negative = true;
+ *result = (value1 >= value2) ?
+ (value1 - value2) : (value2 - value1);
+ }
+ } else {
+ if (valid_bdary_link
+ >= compare) {
+ if (value1 >= value2)
+ *negative = true;
+ *result = (value1 >= value2) ?
+ (value1 - value2) : (value2 - value1);
+ } else {
+ *result = value1 + value2;
+ *negative = true;
+ }
+ }
+}
+
+static u64 roundup_u64(u64 x, u64 y)
+{
+ x += (y - 1);
+ return (div64_ul(x, y) * y);
+}
+
+static u64 rounddown_u64(u64 x, u64 y)
+{
+ u64 rem;
+
+ div64_u64_rem(x, y, &rem);
+ return (x - rem);
+}
+
+static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
+ struct dp_vc_tu_mapping_table *tu_table)
+{
+ u32 const multiplier = 1000000;
+ u64 pclk, lclk;
+ u8 bpp, ln_cnt, link_rate;
+ int run_idx = 0;
+ u32 lwidth, h_blank;
+ u32 fifo_empty = 0;
+ u32 ratio_scale = 1001;
+ u64 temp, ratio, original_ratio;
+ u64 temp2, reminder;
+ u64 temp3, temp4, result = 0;
+
+ u64 err = multiplier;
+ u64 n_err = 0, n_n_err = 0;
+ bool n_err_neg, nn_err_neg;
+ u8 hblank_margin = 16;
+
+ u8 tu_size, tu_size_desired = 0, tu_size_minus1;
+ int valid_boundary_link;
+ u64 resulting_valid;
+ u64 total_valid;
+ u64 effective_valid;
+ u64 effective_valid_recorded;
+ int n_tus;
+ int n_tus_per_lane;
+ int paired_tus;
+ int remainder_tus;
+ int remainder_tus_upper, remainder_tus_lower;
+ int extra_bytes;
+ int filler_size;
+ int delay_start_link;
+ int boundary_moderation_en = 0;
+ int upper_bdry_cnt = 0;
+ int lower_bdry_cnt = 0;
+ int i_upper_bdry_cnt = 0;
+ int i_lower_bdry_cnt = 0;
+ int valid_lower_boundary_link = 0;
+ int even_distribution_bf = 0;
+ int even_distribution_legacy = 0;
+ int even_distribution = 0;
+ int min_hblank = 0;
+ int extra_pclk_cycles;
+ u8 extra_pclk_cycle_delay = 4;
+ int extra_pclk_cycles_in_link_clk;
+ u64 ratio_by_tu;
+ u64 average_valid2;
+ u64 extra_buffer_margin;
+ int new_valid_boundary_link;
+
+ u64 resulting_valid_tmp;
+ u64 ratio_by_tu_tmp;
+ int n_tus_tmp;
+ int extra_pclk_cycles_tmp;
+ int extra_pclk_cycles_in_lclk_tmp;
+ int extra_req_bytes_new_tmp;
+ int filler_size_tmp;
+ int lower_filler_size_tmp;
+ int delay_start_link_tmp;
+ int min_hblank_tmp = 0;
+ bool extra_req_bytes_is_neg = false;
+ struct dp_panel_info *pinfo = &ctrl->panel->pinfo;
+
+ u8 dp_brute_force = 1;
+ u64 brute_force_threshold = 10;
+ u64 diff_abs;
+
+ link_rate = ctrl->link->link_rate;
+ ln_cnt = ctrl->link->lane_count;
+
+ bpp = pinfo->bpp;
+ lwidth = pinfo->h_active;
+ h_blank = pinfo->h_back_porch + pinfo->h_front_porch +
+ pinfo->h_sync_width;
+ pclk = pinfo->pixel_clk_khz * 1000;
+
+ boundary_moderation_en = 0;
+ upper_bdry_cnt = 0;
+ lower_bdry_cnt = 0;
+ i_upper_bdry_cnt = 0;
+ i_lower_bdry_cnt = 0;
+ valid_lower_boundary_link = 0;
+ even_distribution_bf = 0;
+ even_distribution_legacy = 0;
+ even_distribution = 0;
+ min_hblank = 0;
+
+ lclk = link_rate * DP_LINK_RATE_MULTIPLIER;
+
+ pr_debug("pclk=%lld, active_width=%d, h_blank=%d\n",
+ pclk, lwidth, h_blank);
+ pr_debug("lclk = %lld, ln_cnt = %d\n", lclk, ln_cnt);
+ ratio = div64_u64_rem(pclk * bpp * multiplier,
+ 8 * ln_cnt * lclk, &reminder);
+ ratio = div64_u64((pclk * bpp * multiplier), (8 * ln_cnt * lclk));
+ original_ratio = ratio;
+
+ extra_buffer_margin = roundup_u64(div64_u64(extra_pclk_cycle_delay
+ * lclk * multiplier, pclk), multiplier);
+ extra_buffer_margin = div64_u64(extra_buffer_margin, multiplier);
+
+ /* To deal with cases where lines are not distributable */
+ if (((lwidth % ln_cnt) != 0) && ratio < multiplier) {
+ ratio = ratio * ratio_scale;
+ ratio = ratio < (1000 * multiplier)
+ ? ratio : (1000 * multiplier);
+ }
+ pr_debug("ratio = %lld\n", ratio);
+
+ for (tu_size = 32; tu_size <= 64; tu_size++) {
+ temp = ratio * tu_size;
+ temp2 = ((temp / multiplier) + 1) * multiplier;
+ n_err = roundup_u64(temp, multiplier) - temp;
+
+ if (n_err < err) {
+ err = n_err;
+ tu_size_desired = tu_size;
+ }
+ }
+ pr_debug("Info: tu_size_desired = %d\n", tu_size_desired);
+
+ tu_size_minus1 = tu_size_desired - 1;
+
+ valid_boundary_link = roundup_u64(ratio * tu_size_desired, multiplier);
+ valid_boundary_link /= multiplier;
+ n_tus = rounddown((lwidth * bpp * multiplier)
+ / (8 * valid_boundary_link), multiplier) / multiplier;
+ even_distribution_legacy = n_tus % ln_cnt == 0 ? 1 : 0;
+ pr_debug("Info: n_symbol_per_tu=%d, number_of_tus=%d\n",
+ valid_boundary_link, n_tus);
+
+ extra_bytes = roundup_u64((n_tus + 1)
+ * ((valid_boundary_link * multiplier)
+ - (original_ratio * tu_size_desired)), multiplier);
+ extra_bytes /= multiplier;
+ extra_pclk_cycles = roundup(extra_bytes * 8 * multiplier / bpp,
+ multiplier);
+ extra_pclk_cycles /= multiplier;
+ extra_pclk_cycles_in_link_clk = roundup_u64(div64_u64(extra_pclk_cycles
+ * lclk * multiplier, pclk), multiplier);
+ extra_pclk_cycles_in_link_clk /= multiplier;
+ filler_size = roundup_u64((tu_size_desired - valid_boundary_link)
+ * multiplier, multiplier);
+ filler_size /= multiplier;
+ ratio_by_tu = div64_u64(ratio * tu_size_desired, multiplier);
+
+ pr_debug("extra_pclk_cycles_in_link_clk=%d, extra_bytes=%d\n",
+ extra_pclk_cycles_in_link_clk, extra_bytes);
+ pr_debug("extra_pclk_cycles_in_link_clk=%d\n",
+ extra_pclk_cycles_in_link_clk);
+ pr_debug("filler_size=%d, extra_buffer_margin=%lld\n",
+ filler_size, extra_buffer_margin);
+
+ delay_start_link = ((extra_bytes > extra_pclk_cycles_in_link_clk)
+ ? extra_bytes
+ : extra_pclk_cycles_in_link_clk)
+ + filler_size + extra_buffer_margin;
+ resulting_valid = valid_boundary_link;
+ pr_debug("Info: delay_start_link=%d, filler_size=%d\n",
+ delay_start_link, filler_size);
+ pr_debug("valid_boundary_link=%d ratio_by_tu=%lld\n",
+ valid_boundary_link, ratio_by_tu);
+
+ diff_abs = (resulting_valid >= ratio_by_tu)
+ ? (resulting_valid - ratio_by_tu)
+ : (ratio_by_tu - resulting_valid);
+
+ if (err != 0 && ((diff_abs > brute_force_threshold)
+ || (even_distribution_legacy == 0)
+ || (dp_brute_force == 1))) {
+ err = multiplier;
+ for (tu_size = 32; tu_size <= 64; tu_size++) {
+ for (i_upper_bdry_cnt = 1; i_upper_bdry_cnt <= 15;
+ i_upper_bdry_cnt++) {
+ for (i_lower_bdry_cnt = 1;
+ i_lower_bdry_cnt <= 15;
+ i_lower_bdry_cnt++) {
+ new_valid_boundary_link =
+ roundup_u64(ratio
+ * tu_size, multiplier);
+ average_valid2 = (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier))
+ / (i_upper_bdry_cnt
+ + i_lower_bdry_cnt);
+ n_tus = rounddown_u64(div64_u64(lwidth
+ * multiplier * multiplier
+ * (bpp / 8), average_valid2),
+ multiplier);
+ n_tus /= multiplier;
+ n_tus_per_lane
+ = rounddown(n_tus
+ * multiplier
+ / ln_cnt, multiplier);
+ n_tus_per_lane /= multiplier;
+ paired_tus =
+ rounddown((n_tus_per_lane)
+ * multiplier
+ / (i_upper_bdry_cnt
+ + i_lower_bdry_cnt),
+ multiplier);
+ paired_tus /= multiplier;
+ remainder_tus = n_tus_per_lane
+ - paired_tus
+ * (i_upper_bdry_cnt
+ + i_lower_bdry_cnt);
+ if ((remainder_tus
+ - i_upper_bdry_cnt) > 0) {
+ remainder_tus_upper
+ = i_upper_bdry_cnt;
+ remainder_tus_lower =
+ remainder_tus
+ - i_upper_bdry_cnt;
+ } else {
+ remainder_tus_upper
+ = remainder_tus;
+ remainder_tus_lower = 0;
+ }
+ total_valid = paired_tus
+ * (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier))
+ + (remainder_tus_upper
+ * new_valid_boundary_link)
+ + (remainder_tus_lower
+ * (new_valid_boundary_link
+ - multiplier));
+ n_err_neg = nn_err_neg = false;
+ effective_valid
+ = div_u64(total_valid,
+ n_tus_per_lane);
+ n_n_err = (effective_valid
+ >= (ratio * tu_size))
+ ? (effective_valid
+ - (ratio * tu_size))
+ : ((ratio * tu_size)
+ - effective_valid);
+ if (effective_valid < (ratio * tu_size))
+ nn_err_neg = true;
+ n_err = (average_valid2
+ >= (ratio * tu_size))
+ ? (average_valid2
+ - (ratio * tu_size))
+ : ((ratio * tu_size)
+ - average_valid2);
+ if (average_valid2 < (ratio * tu_size))
+ n_err_neg = true;
+ even_distribution =
+ n_tus % ln_cnt == 0 ? 1 : 0;
+ diff_abs =
+ resulting_valid >= ratio_by_tu
+ ? (resulting_valid
+ - ratio_by_tu)
+ : (ratio_by_tu
+ - resulting_valid);
+
+ resulting_valid_tmp = div64_u64(
+ (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier)),
+ (i_upper_bdry_cnt
+ + i_lower_bdry_cnt));
+ ratio_by_tu_tmp =
+ original_ratio * tu_size;
+ ratio_by_tu_tmp /= multiplier;
+ n_tus_tmp = rounddown_u64(
+ div64_u64(lwidth
+ * multiplier * multiplier
+ * bpp / 8,
+ resulting_valid_tmp),
+ multiplier);
+ n_tus_tmp /= multiplier;
+
+ temp3 = (resulting_valid_tmp
+ >= (original_ratio * tu_size))
+ ? (resulting_valid_tmp
+ - original_ratio * tu_size)
+ : (original_ratio * tu_size)
+ - resulting_valid_tmp;
+ temp3 = (n_tus_tmp + 1) * temp3;
+ temp4 = (new_valid_boundary_link
+ >= (original_ratio * tu_size))
+ ? (new_valid_boundary_link
+ - original_ratio
+ * tu_size)
+ : (original_ratio * tu_size)
+ - new_valid_boundary_link;
+ temp4 = (i_upper_bdry_cnt
+ * ln_cnt * temp4);
+
+ temp3 = roundup_u64(temp3, multiplier);
+ temp4 = roundup_u64(temp4, multiplier);
+ dp_ctrl_get_extra_req_bytes
+ (resulting_valid_tmp,
+ new_valid_boundary_link,
+ temp3, temp4,
+ &extra_req_bytes_is_neg,
+ &result,
+ (original_ratio * tu_size));
+ extra_req_bytes_new_tmp
+ = div64_ul(result, multiplier);
+ if ((extra_req_bytes_is_neg)
+ && (extra_req_bytes_new_tmp
+ > 1))
+ extra_req_bytes_new_tmp
+ = extra_req_bytes_new_tmp - 1;
+ if (extra_req_bytes_new_tmp == 0)
+ extra_req_bytes_new_tmp = 1;
+ extra_pclk_cycles_tmp =
+ (u64)(extra_req_bytes_new_tmp
+ * 8 * multiplier) / bpp;
+ extra_pclk_cycles_tmp /= multiplier;
+
+ if (extra_pclk_cycles_tmp <= 0)
+ extra_pclk_cycles_tmp = 1;
+ extra_pclk_cycles_in_lclk_tmp =
+ roundup_u64(div64_u64(
+ extra_pclk_cycles_tmp
+ * lclk * multiplier,
+ pclk), multiplier);
+ extra_pclk_cycles_in_lclk_tmp
+ /= multiplier;
+ filler_size_tmp = roundup_u64(
+ (tu_size * multiplier *
+ new_valid_boundary_link),
+ multiplier);
+ filler_size_tmp /= multiplier;
+ lower_filler_size_tmp =
+ filler_size_tmp + 1;
+ if (extra_req_bytes_is_neg)
+ temp3 = (extra_req_bytes_new_tmp
+ > extra_pclk_cycles_in_lclk_tmp
+ ? extra_pclk_cycles_in_lclk_tmp
+ : extra_req_bytes_new_tmp);
+ else
+ temp3 = (extra_req_bytes_new_tmp
+ > extra_pclk_cycles_in_lclk_tmp
+ ? extra_req_bytes_new_tmp :
+ extra_pclk_cycles_in_lclk_tmp);
+
+ temp4 = lower_filler_size_tmp
+ + extra_buffer_margin;
+ if (extra_req_bytes_is_neg)
+ delay_start_link_tmp
+ = (temp3 >= temp4)
+ ? (temp3 - temp4)
+ : (temp4 - temp3);
+ else
+ delay_start_link_tmp
+ = temp3 + temp4;
+
+ min_hblank_tmp = (int)div64_u64(
+ roundup_u64(
+ div64_u64(delay_start_link_tmp
+ * pclk * multiplier, lclk),
+ multiplier), multiplier)
+ + hblank_margin;
+
+ if (((even_distribution == 1)
+ || ((even_distribution_bf == 0)
+ && (even_distribution_legacy
+ == 0)))
+ && !n_err_neg && !nn_err_neg
+ && n_n_err < err
+ && (n_n_err < diff_abs
+ || (dp_brute_force == 1))
+ && (new_valid_boundary_link
+ - 1) > 0
+ && (h_blank >=
+ (u32)min_hblank_tmp)) {
+ upper_bdry_cnt =
+ i_upper_bdry_cnt;
+ lower_bdry_cnt =
+ i_lower_bdry_cnt;
+ err = n_n_err;
+ boundary_moderation_en = 1;
+ tu_size_desired = tu_size;
+ valid_boundary_link =
+ new_valid_boundary_link;
+ effective_valid_recorded
+ = effective_valid;
+ delay_start_link
+ = delay_start_link_tmp;
+ filler_size = filler_size_tmp;
+ min_hblank = min_hblank_tmp;
+ n_tus = n_tus_tmp;
+ even_distribution_bf = 1;
+
+ pr_debug("upper_bdry_cnt=%d, lower_boundary_cnt=%d, err=%lld, tu_size_desired=%d, valid_boundary_link=%d, effective_valid=%lld\n",
+ upper_bdry_cnt,
+ lower_bdry_cnt, err,
+ tu_size_desired,
+ valid_boundary_link,
+ effective_valid);
+ }
+ }
+ }
+ }
+
+ if (boundary_moderation_en == 1) {
+ resulting_valid = (u64)(upper_bdry_cnt
+ *valid_boundary_link + lower_bdry_cnt
+ * (valid_boundary_link - 1))
+ / (upper_bdry_cnt + lower_bdry_cnt);
+ ratio_by_tu = original_ratio * tu_size_desired;
+ valid_lower_boundary_link =
+ (valid_boundary_link / multiplier) - 1;
+
+ tu_size_minus1 = tu_size_desired - 1;
+ even_distribution_bf = 1;
+ valid_boundary_link /= multiplier;
+ pr_debug("Info: Boundary_moderation enabled\n");
+ }
+ }
+
+ min_hblank = ((int) roundup_u64(div64_u64(delay_start_link * pclk
+ * multiplier, lclk), multiplier))
+ / multiplier + hblank_margin;
+ if (h_blank < (u32)min_hblank) {
+ pr_debug(" WARNING: run_idx=%d Programmed h_blank %d is smaller than the min_hblank %d supported.\n",
+ run_idx, h_blank, min_hblank);
+ }
+
+ if (fifo_empty) {
+ tu_size_minus1 = 31;
+ valid_boundary_link = 32;
+ delay_start_link = 0;
+ boundary_moderation_en = 0;
+ }
+
+ pr_debug("tu_size_minus1=%d valid_boundary_link=%d delay_start_link=%d boundary_moderation_en=%d\n upper_boundary_cnt=%d lower_boundary_cnt=%d valid_lower_boundary_link=%d min_hblank=%d\n",
+ tu_size_minus1, valid_boundary_link, delay_start_link,
+ boundary_moderation_en, upper_bdry_cnt, lower_bdry_cnt,
+ valid_lower_boundary_link, min_hblank);
+
+ tu_table->valid_boundary_link = valid_boundary_link;
+ tu_table->delay_start_link = delay_start_link;
+ tu_table->boundary_moderation_en = boundary_moderation_en;
+ tu_table->valid_lower_boundary_link = valid_lower_boundary_link;
+ tu_table->upper_boundary_count = upper_bdry_cnt;
+ tu_table->lower_boundary_count = lower_bdry_cnt;
+ tu_table->tu_size_minus1 = tu_size_minus1;
+}
+
+static void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
+{
+ u32 dp_tu = 0x0;
+ u32 valid_boundary = 0x0;
+ u32 valid_boundary2 = 0x0;
+ struct dp_vc_tu_mapping_table tu_calc_table;
+
+ dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
+
+ dp_tu |= tu_calc_table.tu_size_minus1;
+ valid_boundary |= tu_calc_table.valid_boundary_link;
+ valid_boundary |= (tu_calc_table.delay_start_link << 16);
+
+ valid_boundary2 |= (tu_calc_table.valid_lower_boundary_link << 1);
+ valid_boundary2 |= (tu_calc_table.upper_boundary_count << 16);
+ valid_boundary2 |= (tu_calc_table.lower_boundary_count << 20);
+
+ if (tu_calc_table.boundary_moderation_en)
+ valid_boundary2 |= BIT(0);
+
+ pr_debug("dp_tu=0x%x, valid_boundary=0x%x, valid_boundary2=0x%x\n",
+ dp_tu, valid_boundary, valid_boundary2);
+
+ ctrl->catalog->dp_tu = dp_tu;
+ ctrl->catalog->valid_boundary = valid_boundary;
+ ctrl->catalog->valid_boundary2 = valid_boundary2;
+
+ ctrl->catalog->update_transfer_unit(ctrl->catalog);
+}
+
+static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (ctrl->cont_splash)
+ return ret;
+
+ ret = wait_for_completion_timeout(&ctrl->video_comp, HZ / 2);
+ if (ret <= 0) {
+ pr_err("Link Train timedout\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl,
+ u32 voltage_level, u32 pre_emphasis_level)
+{
+ int i;
+ u8 buf[4];
+ u32 max_level_reached = 0;
+
+ if (voltage_level == DP_LINK_VOLTAGE_MAX) {
+ pr_debug("max. voltage swing level reached %d\n",
+ voltage_level);
+ max_level_reached |= BIT(2);
+ }
+
+ if (pre_emphasis_level == DP_LINK_PRE_EMPHASIS_MAX) {
+ pr_debug("max. pre-emphasis level reached %d\n",
+ pre_emphasis_level);
+ max_level_reached |= BIT(5);
+ }
+
+ pr_debug("max_level_reached = 0x%x\n", max_level_reached);
+
+ pre_emphasis_level <<= 3;
+
+ for (i = 0; i < 4; i++)
+ buf[i] = voltage_level | pre_emphasis_level | max_level_reached;
+
+ pr_debug("p|v=0x%x\n", voltage_level | pre_emphasis_level);
+ return ctrl->aux->write(ctrl->aux, 0x103, 4, AUX_NATIVE, buf);
+}
+
+static void dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
+{
+ struct dp_link *link = ctrl->link;
+
+ pr_debug("v=%d p=%d\n", link->v_level, link->p_level);
+
+ ctrl->catalog->update_vx_px(ctrl->catalog,
+ link->v_level, link->p_level);
+
+ dp_ctrl_update_sink_vx_px(ctrl, link->v_level, link->p_level);
+}
+
+static void dp_ctrl_cap_lane_rate_set(struct dp_ctrl_private *ctrl)
+{
+ u8 buf[4];
+ struct dp_panel_dpcd *cap;
+
+ cap = &ctrl->panel->dpcd;
+
+ pr_debug("bw=%x lane=%d\n", ctrl->link->link_rate,
+ ctrl->link->lane_count);
+
+ buf[0] = ctrl->link->link_rate;
+ buf[1] = ctrl->link->lane_count;
+
+ if (cap->enhanced_frame)
+ buf[1] |= 0x80;
+
+ ctrl->aux->write(ctrl->aux, 0x100, 2, AUX_NATIVE, buf);
+}
+
+static void dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
+ u8 pattern)
+{
+ u8 buf[4];
+
+ pr_debug("pattern=%x\n", pattern);
+
+ buf[0] = pattern;
+ ctrl->aux->write(ctrl->aux, 0x102, 1, AUX_NATIVE, buf);
+}
+
+static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
+{
+ int tries, old_v_level;
+ int ret = 0;
+ int usleep_time;
+ int const maximum_retries = 5;
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+ ctrl->catalog->set_pattern(ctrl->catalog, 0x01);
+ dp_ctrl_cap_lane_rate_set(ctrl);
+ dp_ctrl_train_pattern_set(ctrl, 0x21); /* train_1 */
+ dp_ctrl_update_vx_px(ctrl);
+
+ tries = 0;
+ old_v_level = ctrl->link->v_level;
+ while (1) {
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+
+ if (ctrl->link->clock_recovery(ctrl->link)) {
+ ret = 0;
+ break;
+ }
+
+ if (ctrl->link->v_level == DP_LINK_VOLTAGE_MAX) {
+ ret = -1;
+ break; /* quit */
+ }
+
+ if (old_v_level == ctrl->link->v_level) {
+ tries++;
+ if (tries >= maximum_retries) {
+ ret = -1;
+ break; /* quit */
+ }
+ } else {
+ tries = 0;
+ old_v_level = ctrl->link->v_level;
+ }
+
+ ctrl->link->adjust_levels(ctrl->link);
+
+ dp_ctrl_update_vx_px(ctrl);
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (!ctrl)
+ return -EINVAL;
+
+ switch (ctrl->link->link_rate) {
+ case DP_LINK_RATE_810:
+ ctrl->link->link_rate = DP_LINK_RATE_540;
+ break;
+ case DP_LINK_RATE_540:
+ ctrl->link->link_rate = DP_LINK_RATE_270;
+ break;
+ case DP_LINK_RATE_270:
+ ctrl->link->link_rate = DP_LINK_RATE_162;
+ break;
+ case DP_LINK_RATE_162:
+ default:
+ ret = -EINVAL;
+ break;
+ };
+
+ pr_debug("new rate=%d\n", ctrl->link->link_rate);
+
+ return ret;
+}
+
+static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
+{
+ int usleep_time;
+
+ dp_ctrl_train_pattern_set(ctrl, 0);
+
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+}
+
+static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
+{
+ int tries = 0;
+ int ret = 0;
+ int usleep_time;
+ char pattern;
+ int const maximum_retries = 5;
+
+ if (ctrl->panel->dpcd.flags & DPCD_TPS3)
+ pattern = 0x03;
+ else
+ pattern = 0x02;
+
+ dp_ctrl_update_vx_px(ctrl);
+ ctrl->catalog->set_pattern(ctrl->catalog, pattern);
+ dp_ctrl_train_pattern_set(ctrl, pattern | 0x20);
+
+ do {
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+
+ if (ctrl->link->channel_equalization(ctrl->link)) {
+ ret = 0;
+ break;
+ }
+
+ if (tries > maximum_retries) {
+ ret = -1;
+ break;
+ }
+ tries++;
+
+ ctrl->link->adjust_levels(ctrl->link);
+
+ dp_ctrl_update_vx_px(ctrl);
+ } while (1);
+
+ return ret;
+}
+
+static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ ret = ctrl->aux->ready(ctrl->aux);
+ if (!ret) {
+ pr_err("aux chan NOT ready\n");
+ return ret;
+ }
+
+ ctrl->link->p_level = 0;
+ ctrl->link->v_level = 0;
+
+ dp_ctrl_config_ctrl(ctrl);
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ ret = dp_ctrl_link_train_1(ctrl);
+ if (ret < 0) {
+ if (!dp_ctrl_link_rate_down_shift(ctrl)) {
+ pr_debug("retry with lower rate\n");
+
+ dp_ctrl_clear_training_pattern(ctrl);
+ return -EAGAIN;
+ }
+
+ pr_err("Training 1 failed\n");
+ ret = -EINVAL;
+ goto clear;
+ }
+
+ pr_debug("Training 1 completed successfully\n");
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+ ret = dp_ctrl_link_training_2(ctrl);
+ if (ret < 0) {
+ if (!dp_ctrl_link_rate_down_shift(ctrl)) {
+ pr_debug("retry with lower rate\n");
+
+ dp_ctrl_clear_training_pattern(ctrl);
+ return -EAGAIN;
+ }
+
+ pr_err("Training 2 failed\n");
+ ret = -EINVAL;
+ goto clear;
+ }
+
+ pr_debug("Training 2 completed successfully\n");
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+clear:
+ dp_ctrl_clear_training_pattern(ctrl);
+ return ret;
+}
+
+static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
+{
+ bool mainlink_ready = false;
+ int ret = 0;
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+ dp_ctrl_set_sink_power_state(ctrl, SINK_POWER_ON);
+
+ if (ctrl->link->phy_pattern_requested(ctrl->link))
+ goto end;
+
+ if (!train)
+ goto send_video;
+
+ /*
+ * As part of previous calls, DP controller state might have
+ * transitioned to PUSH_IDLE. In order to start transmitting a link
+ * training pattern, we have to first to a DP software reset.
+ */
+ ctrl->catalog->reset(ctrl->catalog);
+
+ ret = dp_ctrl_link_train(ctrl);
+ if (ret)
+ goto end;
+
+send_video:
+ /*
+ * Set up transfer unit values and set controller state to send
+ * video.
+ */
+ dp_ctrl_setup_tr_unit(ctrl);
+ ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
+
+ dp_ctrl_wait4video_ready(ctrl);
+ mainlink_ready = ctrl->catalog->mainlink_ready(ctrl->catalog);
+ pr_debug("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
+end:
+ return ret;
+}
+
+static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
+ char *name, u32 rate)
+{
+ u32 num = ctrl->parser->mp[DP_CTRL_PM].num_clk;
+ struct dss_clk *cfg = ctrl->parser->mp[DP_CTRL_PM].clk_config;
+
+ while (num && strcmp(cfg->clk_name, name)) {
+ num--;
+ cfg++;
+ }
+
+ if (num)
+ cfg->rate = rate;
+ else
+ pr_err("%s clock could not be set with rate %d\n", name, rate);
+}
+
+static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ ctrl->power->set_pixel_clk_parent(ctrl->power);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
+ (ctrl->link->link_rate * DP_LINK_RATE_MULTIPLIER) /
+ DP_KHZ_TO_HZ);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_crypto_clk", DP_CRYPTO_CLK_RATE_KHZ);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_pixel_clk", ctrl->pixel_rate);
+
+ ret = ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, true);
+ if (ret) {
+ pr_err("Unabled to start link clocks\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_disable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+ return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
+}
+
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip)
+{
+ struct dp_ctrl_private *ctrl;
+ struct dp_catalog_ctrl *catalog;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->initialized) {
+ pr_debug("host init done already\n");
+ return 0;
+ }
+
+ ctrl->orientation = flip;
+ catalog = ctrl->catalog;
+
+ catalog->reset(ctrl->catalog);
+ catalog->phy_reset(ctrl->catalog);
+ catalog->enable_irq(ctrl->catalog, true);
+
+ ctrl->initialized = true;
+
+ return 0;
+}
+
+/**
+ * dp_ctrl_host_deinit() - Uninitialize DP controller
+ * @ctrl: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
+{
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (!ctrl->initialized) {
+ pr_debug("host deinit done already\n");
+ return;
+ }
+
+ ctrl->catalog->enable_irq(ctrl->catalog, false);
+ ctrl->catalog->reset(ctrl->catalog);
+
+ /* Make sure DP is disabled before clk disable */
+ wmb();
+
+ dp_ctrl_disable_mainlink_clocks(ctrl);
+
+ ctrl->initialized = false;
+ pr_debug("Host deinitialized successfully\n");
+}
+
+static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)
+{
+ int ret = 0;
+
+ do {
+ if (ret == -EAGAIN)
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+ ctrl->orientation, ctrl->link->lane_count);
+
+ if (lt_needed) {
+ /*
+ * Diasable and re-enable the mainlink clock since the
+ * link clock might have been adjusted as part of the
+ * link maintenance.
+ */
+ if (!ctrl->link->phy_pattern_requested(
+ ctrl->link))
+ dp_ctrl_disable_mainlink_clocks(ctrl);
+
+ ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+ if (ret)
+ continue;
+ }
+
+ dp_ctrl_configure_source_params(ctrl);
+
+ reinit_completion(&ctrl->idle_comp);
+
+ ctrl->power_on = true;
+
+ if (ctrl->psm_enabled) {
+ ret = ctrl->link->send_psm_request(ctrl->link, false);
+ if (ret) {
+ pr_err("failed to exit low power mode, rc=%d\n",
+ ret);
+ continue;
+ }
+ }
+
+ ret = dp_ctrl_setup_main_link(ctrl, lt_needed);
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (ctrl->cont_splash)
+ goto link_training;
+
+ ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
+ ctrl->catalog->hpd_config(ctrl->catalog, true);
+
+ ctrl->link->link_rate = ctrl->panel->get_link_rate(ctrl->panel);
+ ctrl->link->lane_count = ctrl->panel->dpcd.max_lane_count;
+ ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+
+ pr_debug("link_rate=%d, lane_count=%d, pixel_rate=%d\n",
+ ctrl->link->link_rate, ctrl->link->lane_count,
+ ctrl->pixel_rate);
+
+ ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+ ctrl->orientation, ctrl->link->lane_count);
+
+ ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+ if (ret)
+ goto exit;
+
+ reinit_completion(&ctrl->idle_comp);
+
+ dp_ctrl_configure_source_params(ctrl);
+
+ if (ctrl->psm_enabled)
+ ret = ctrl->link->send_psm_request(ctrl->link, false);
+link_training:
+ ctrl->power_on = true;
+
+ while (-EAGAIN == dp_ctrl_setup_main_link(ctrl, true))
+ pr_debug("MAIN LINK TRAINING RETRY\n");
+
+ ctrl->cont_splash = 0;
+
+ ctrl->power_on = true;
+ pr_debug("End-\n");
+
+exit:
+ return ret;
+}
+
+static int dp_ctrl_off_irq(struct dp_ctrl_private *ctrl)
+{
+ if (!ctrl->power_on) {
+ pr_debug("ctrl already powered off\n");
+ return 0;
+ }
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ /* Make sure DP mainlink and audio engines are disabled */
+ wmb();
+
+ complete_all(&ctrl->irq_comp);
+ pr_debug("end\n");
+
+ return 0;
+}
+
+static int dp_ctrl_off_hpd(struct dp_ctrl_private *ctrl)
+{
+ if (!ctrl->power_on) {
+ pr_debug("panel already powered off\n");
+ return 0;
+ }
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ ctrl->power_on = false;
+ ctrl->sink_info_read = false;
+
+ pr_debug("DP off done\n");
+
+ return 0;
+}
+
+static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->hpd_irq_on)
+ rc = dp_ctrl_on_irq(ctrl, false);
+ else
+ rc = dp_ctrl_on_hpd(ctrl);
+end:
+ return rc;
+}
+
+static int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->hpd_irq_on)
+ rc = dp_ctrl_off_irq(ctrl);
+ else
+ rc = dp_ctrl_off_hpd(ctrl);
+end:
+ return rc;
+}
+
+static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl, u32 irq)
+{
+ u32 isr;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl)
+ return;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ isr = ctrl->catalog->get_interrupt(ctrl->catalog);
+
+ if (isr & DP_CTRL_INTR_READY_FOR_VIDEO)
+ dp_ctrl_video_ready(ctrl);
+
+ if (isr & DP_CTRL_INTR_IDLE_PATTERN_SENT)
+ dp_ctrl_idle_patterns_sent(ctrl);
+}
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+ struct dp_ctrl *dp_ctrl;
+
+ if (!in->dev || !in->panel || !in->aux ||
+ !in->link || !in->catalog) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ ctrl = devm_kzalloc(in->dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ init_completion(&ctrl->idle_comp);
+ init_completion(&ctrl->video_comp);
+ init_completion(&ctrl->irq_comp);
+
+ /* in parameters */
+ ctrl->parser = in->parser;
+ ctrl->panel = in->panel;
+ ctrl->power = in->power;
+ ctrl->aux = in->aux;
+ ctrl->link = in->link;
+ ctrl->catalog = in->catalog;
+
+ dp_ctrl = &ctrl->dp_ctrl;
+
+ /* out parameters */
+ dp_ctrl->init = dp_ctrl_host_init;
+ dp_ctrl->deinit = dp_ctrl_host_deinit;
+ dp_ctrl->on = dp_ctrl_on;
+ dp_ctrl->off = dp_ctrl_off;
+ dp_ctrl->push_idle = dp_ctrl_push_idle;
+ dp_ctrl->isr = dp_ctrl_isr;
+
+ return dp_ctrl;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl)
+{
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl)
+ return;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ devm_kfree(ctrl->dev, ctrl);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
new file mode 100644
index 0000000..c9cf7f8
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -0,0 +1,47 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_CTRL_H_
+#define _DP_CTRL_H_
+
+#include "dp_aux.h"
+#include "dp_panel.h"
+#include "dp_link.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+
+struct dp_ctrl {
+ int (*init)(struct dp_ctrl *dp_ctrl, bool flip);
+ void (*deinit)(struct dp_ctrl *dp_ctrl);
+ int (*on)(struct dp_ctrl *dp_ctrl);
+ int (*off)(struct dp_ctrl *dp_ctrl);
+ void (*push_idle)(struct dp_ctrl *dp_ctrl);
+ void (*isr)(struct dp_ctrl *dp_ctrl, u32 isr);
+};
+
+struct dp_ctrl_in {
+ struct device *dev;
+ struct dp_panel *panel;
+ struct dp_aux *aux;
+ struct dp_link *link;
+ struct dp_parser *parser;
+ struct dp_power *power;
+ struct dp_catalog_ctrl *catalog;
+};
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
+
+#endif /* _DP_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
new file mode 100644
index 0000000..e3c30a5
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -0,0 +1,683 @@
+/*
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp]: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/component.h>
+#include <linux/of_irq.h>
+
+#include "dp_usbpd.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_panel.h"
+#include "dp_ctrl.h"
+#include "dp_display.h"
+
+static struct dp_display *g_dp_display;
+
+struct dp_display_private {
+ char *name;
+ int irq;
+
+ struct platform_device *pdev;
+ struct dentry *root;
+ struct mutex lock;
+
+ struct dp_usbpd *usbpd;
+ struct dp_parser *parser;
+ struct dp_power *power;
+ struct dp_catalog *catalog;
+ struct dp_aux *aux;
+ struct dp_link *link;
+ struct dp_panel *panel;
+ struct dp_ctrl *ctrl;
+
+ struct dp_usbpd_cb usbpd_cb;
+ struct dp_display_mode mode;
+ struct dp_display dp_display;
+};
+
+static const struct of_device_id dp_dt_match[] = {
+ {.compatible = "qcom,dp-display"},
+ {}
+};
+
+static ssize_t debugfs_dp_info_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ struct dp_display_private *dp = file->private_data;
+ char *buf;
+ u32 len = 0;
+
+ if (!dp)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(SZ_4K, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf + len, (SZ_4K - len), "name = %s\n", dp->name);
+ len += snprintf(buf + len, (SZ_4K - len),
+ "\tResolution = %dx%d\n",
+ dp->panel->pinfo.h_active,
+ dp->panel->pinfo.v_active);
+
+ if (copy_to_user(buff, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+ return len;
+}
+
+static const struct file_operations dp_debug_fops = {
+ .open = simple_open,
+ .read = debugfs_dp_info_read,
+};
+
+static int dp_display_debugfs_init(struct dp_display_private *dp)
+{
+ int rc = 0;
+ struct dentry *dir, *file;
+
+ dir = debugfs_create_dir(dp->name, NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ rc = PTR_ERR(dir);
+ pr_err("[%s] debugfs create dir failed, rc = %d\n",
+ dp->name, rc);
+ goto error;
+ }
+
+ file = debugfs_create_file("dp_debug", 0444, dir, dp, &dp_debug_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs create file failed, rc=%d\n",
+ dp->name, rc);
+ goto error_remove_dir;
+ }
+
+ dp->root = dir;
+ return rc;
+error_remove_dir:
+ debugfs_remove(dir);
+error:
+ return rc;
+}
+
+static int dp_display_debugfs_deinit(struct dp_display_private *dp)
+{
+ debugfs_remove(dp->root);
+ return 0;
+}
+
+static int dp_display_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+ struct drm_device *drm;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (!dev || !pdev || !master) {
+ pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
+ dev, pdev, master);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ drm = dev_get_drvdata(master);
+ dp = platform_get_drvdata(pdev);
+ if (!drm || !dp) {
+ pr_err("invalid param(s), drm %pK, dp %pK\n",
+ drm, dp);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp->dp_display.drm_dev = drm;
+
+ mutex_lock(&dp->lock);
+
+ rc = dp_display_debugfs_init(dp);
+ if (rc) {
+ pr_err("[%s]Debugfs init failed, rc=%d\n", dp->name, rc);
+ goto end;
+ }
+
+ rc = dp->parser->parse(dp->parser);
+ if (rc) {
+ pr_err("device tree parsing failed\n");
+ goto end;
+ }
+end:
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static void dp_display_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dp_display_private *dp;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (!dev || !pdev) {
+ pr_err("invalid param(s)\n");
+ return;
+ }
+
+ dp = platform_get_drvdata(pdev);
+ if (!dp) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ mutex_lock(&dp->lock);
+
+ (void)dp_display_debugfs_deinit(dp);
+
+ mutex_unlock(&dp->lock);
+}
+
+static const struct component_ops dp_display_comp_ops = {
+ .bind = dp_display_bind,
+ .unbind = dp_display_unbind,
+};
+
+static int dp_display_process_hpd_high(struct dp_display_private *dp)
+{
+ int rc;
+
+ rc = dp->panel->read_dpcd(dp->panel);
+ if (rc)
+ goto end;
+
+ rc = dp->panel->read_edid(dp->panel);
+ if (rc)
+ goto end;
+
+ return 0;
+end:
+ return rc;
+}
+
+static int dp_display_process_hpd_low(struct dp_display_private *dp)
+{
+ return 0;
+}
+
+static irqreturn_t dp_display_irq(int irq, void *dev_id)
+{
+ struct dp_display_private *dp = dev_id;
+
+ if (!dp) {
+ pr_err("invalid data\n");
+ return IRQ_NONE;
+ }
+
+ dp->aux->isr(dp->aux);
+
+ return IRQ_HANDLED;
+}
+
+static int dp_display_usbpd_configure_cb(struct device *dev)
+{
+ int rc = 0;
+ bool flip = false;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ if (!dp->irq) {
+ dp->irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (dp->irq < 0) {
+ rc = dp->irq;
+ pr_err("failed to get irq: %d\n", rc);
+ goto end;
+ }
+
+ rc = devm_request_irq(dev, dp->irq,
+ dp_display_irq, IRQF_TRIGGER_HIGH,
+ "dp_display_isr", dp);
+ if (rc < 0) {
+ pr_err("failed to request IRQ%u: %d\n",
+ dp->irq, rc);
+ goto end;
+ }
+ }
+
+ mutex_lock(&dp->lock);
+
+ if (dp->usbpd->orientation == ORIENTATION_CC2)
+ flip = true;
+
+ dp->power->init(dp->power, flip);
+ dp->ctrl->init(dp->ctrl, flip);
+ dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+ if (dp->usbpd->hpd_high)
+ dp_display_process_hpd_high(dp);
+
+ mutex_unlock(&dp->lock);
+end:
+ return rc;
+}
+
+static int dp_display_usbpd_disconnect_cb(struct device *dev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ mutex_lock(&dp->lock);
+ mutex_unlock(&dp->lock);
+
+end:
+ return rc;
+}
+
+static int dp_display_usbpd_attention_cb(struct device *dev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ mutex_lock(&dp->lock);
+
+ if (dp->usbpd->hpd_irq) {
+ if (!dp->link->process_request(dp->link))
+ goto end;
+ }
+
+ if (dp->usbpd->hpd_high)
+ dp_display_process_hpd_high(dp);
+ else
+ dp_display_process_hpd_low(dp);
+
+ mutex_unlock(&dp->lock);
+end:
+ return rc;
+}
+
+static int dp_init_sub_modules(struct dp_display_private *dp)
+{
+ int rc = 0;
+ struct device *dev = &dp->pdev->dev;
+ struct dp_usbpd_cb *cb = &dp->usbpd_cb;
+ struct dp_ctrl_in ctrl_in = {
+ .dev = dev,
+ };
+
+ cb->configure = dp_display_usbpd_configure_cb;
+ cb->disconnect = dp_display_usbpd_disconnect_cb;
+ cb->attention = dp_display_usbpd_attention_cb;
+
+ dp->usbpd = dp_usbpd_get(dev, cb);
+ if (IS_ERR(dp->usbpd)) {
+ rc = PTR_ERR(dp->usbpd);
+ pr_err("failed to initialize usbpd, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->parser = dp_parser_get(dp->pdev);
+ if (IS_ERR(dp->parser)) {
+ rc = PTR_ERR(dp->parser);
+ pr_err("failed to initialize parser, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->catalog = dp_catalog_get(dev, &dp->parser->io);
+ if (IS_ERR(dp->catalog)) {
+ rc = PTR_ERR(dp->catalog);
+ pr_err("failed to initialize catalog, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->power = dp_power_get(dp->parser);
+ if (IS_ERR(dp->power)) {
+ rc = PTR_ERR(dp->power);
+ pr_err("failed to initialize power, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->aux = dp_aux_get(dev, &dp->catalog->aux);
+ if (IS_ERR(dp->aux)) {
+ rc = PTR_ERR(dp->aux);
+ pr_err("failed to initialize aux, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->panel = dp_panel_get(dev, dp->aux, &dp->catalog->panel);
+ if (IS_ERR(dp->panel)) {
+ rc = PTR_ERR(dp->panel);
+ pr_err("failed to initialize panel, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->link = dp_link_get(dev, dp->aux);
+ if (IS_ERR(dp->link)) {
+ rc = PTR_ERR(dp->link);
+ pr_err("failed to initialize link, rc = %d\n", rc);
+ goto err;
+ }
+
+ ctrl_in.link = dp->link;
+ ctrl_in.panel = dp->panel;
+ ctrl_in.aux = dp->aux;
+ ctrl_in.power = dp->power;
+ ctrl_in.catalog = &dp->catalog->ctrl;
+ ctrl_in.parser = dp->parser;
+
+ dp->ctrl = dp_ctrl_get(&ctrl_in);
+ if (IS_ERR(dp->ctrl)) {
+ rc = PTR_ERR(dp->ctrl);
+ pr_err("failed to initialize ctrl, rc = %d\n", rc);
+ goto err;
+ }
+err:
+ return rc;
+}
+
+static int dp_display_set_mode(struct dp_display *dp_display,
+ struct dp_display_mode *mode)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ dp->panel->pinfo = mode->timing;
+ dp->panel->init_info(dp->panel);
+error:
+ return rc;
+}
+
+static int dp_display_prepare(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_enable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+ dp->ctrl->on(dp->ctrl);
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_post_enable(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_pre_disable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+
+ dp->ctrl->off(dp->ctrl);
+
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_disable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+
+ dp->aux->deinit(dp->aux);
+ dp->ctrl->deinit(dp->ctrl);
+ dp->power->deinit(dp->power);
+
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_unprepare(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_validate_mode(struct dp_display *dp,
+ struct dp_display_mode *mode)
+{
+ return 0;
+}
+
+static int dp_display_get_modes(struct dp_display *dp,
+ struct dp_display_mode *modes, u32 *count)
+{
+ *count = 1;
+
+ if (modes) {
+ modes->timing.h_active = 1920;
+ modes->timing.v_active = 1080;
+ modes->timing.h_back_porch = 148;
+ modes->timing.h_front_porch = 88;
+ modes->timing.h_sync_width = 44;
+ modes->timing.h_active_low = 0;
+ modes->timing.v_back_porch = 36;
+ modes->timing.v_front_porch = 4;
+ modes->timing.v_sync_width = 5;
+ modes->timing.v_active_low = 0;
+ modes->timing.h_skew = 0;
+ modes->timing.refresh_rate = 60;
+ modes->timing.pixel_clk_khz = 148500;
+ }
+
+ return 0;
+}
+
+static int dp_display_detect(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("pdev not found\n");
+ return -ENODEV;
+ }
+
+ dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ mutex_init(&dp->lock);
+ dp->pdev = pdev;
+ dp->name = "drm_dp";
+
+ rc = dp_init_sub_modules(dp);
+ if (rc) {
+ devm_kfree(&pdev->dev, dp);
+ return -EPROBE_DEFER;
+ }
+
+ platform_set_drvdata(pdev, dp);
+
+ g_dp_display = &dp->dp_display;
+
+ g_dp_display->enable = dp_display_enable;
+ g_dp_display->post_enable = dp_display_post_enable;
+ g_dp_display->pre_disable = dp_display_pre_disable;
+ g_dp_display->disable = dp_display_disable;
+ g_dp_display->set_mode = dp_display_set_mode;
+ g_dp_display->validate_mode = dp_display_validate_mode;
+ g_dp_display->get_modes = dp_display_get_modes;
+ g_dp_display->detect = dp_display_detect;
+ g_dp_display->prepare = dp_display_prepare;
+ g_dp_display->unprepare = dp_display_unprepare;
+
+ rc = component_add(&pdev->dev, &dp_display_comp_ops);
+ if (rc)
+ pr_err("component add failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+struct dp_display *dp_display_get(void)
+{
+ return g_dp_display;
+}
+
+static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
+{
+ dp_ctrl_put(dp->ctrl);
+ dp_link_put(dp->link);
+ dp_panel_put(dp->panel);
+ dp_aux_put(dp->aux);
+ dp_power_put(dp->power);
+ dp_catalog_put(dp->catalog);
+ dp_parser_put(dp->parser);
+ dp_usbpd_put(dp->usbpd);
+}
+
+static int dp_display_remove(struct platform_device *pdev)
+{
+ struct dp_display_private *dp;
+
+ if (!pdev)
+ return -EINVAL;
+
+ dp = platform_get_drvdata(pdev);
+
+ dp_display_deinit_sub_modules(dp);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, dp);
+
+ return 0;
+}
+
+static struct platform_driver dp_display_driver = {
+ .probe = dp_display_probe,
+ .remove = dp_display_remove,
+ .driver = {
+ .name = "msm-dp-display",
+ .of_match_table = dp_dt_match,
+ },
+};
+
+static int __init dp_display_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&dp_display_driver);
+ if (ret) {
+ pr_err("driver register failed");
+ return ret;
+ }
+
+ return ret;
+}
+module_init(dp_display_init);
+
+static void __exit dp_display_cleanup(void)
+{
+ platform_driver_unregister(&dp_display_driver);
+}
+module_exit(dp_display_cleanup);
+
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
new file mode 100644
index 0000000..2bc591c
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -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.
+ *
+ */
+
+#ifndef _DP_DISPLAY_H_
+#define _DP_DISPLAY_H_
+
+#include <drm/drmP.h>
+
+#include "dp_panel.h"
+
+struct dp_display_mode {
+ struct dp_panel_info timing;
+ u32 capabilities;
+};
+
+struct dp_display {
+ struct drm_device *drm_dev;
+ struct dp_bridge *bridge;
+
+ int (*enable)(struct dp_display *dp_display);
+ int (*post_enable)(struct dp_display *dp_display);
+
+ int (*pre_disable)(struct dp_display *dp_display);
+ int (*disable)(struct dp_display *dp_display);
+
+ int (*set_mode)(struct dp_display *dp_display,
+ struct dp_display_mode *mode);
+ int (*validate_mode)(struct dp_display *dp_display,
+ struct dp_display_mode *mode);
+ int (*get_modes)(struct dp_display *dp_display,
+ struct dp_display_mode *modes, u32 *count);
+
+ int (*detect)(struct dp_display *dp_display);
+
+ int (*prepare)(struct dp_display *dp_display);
+ int (*unprepare)(struct dp_display *dp_display);
+};
+
+struct dp_display *dp_display_get(void);
+#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
new file mode 100644
index 0000000..85bc574
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -0,0 +1,467 @@
+/*
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp]: %s: " fmt, __func__
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "sde_connector.h"
+#include "dp_drm.h"
+
+#define to_dp_bridge(x) container_of((x), struct dp_bridge, base)
+
+static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
+ struct dp_display_mode *dp_mode)
+{
+ memset(dp_mode, 0, sizeof(*dp_mode));
+
+ dp_mode->timing.h_active = drm_mode->hdisplay;
+ dp_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
+ dp_mode->timing.h_sync_width = drm_mode->htotal -
+ (drm_mode->hsync_start + dp_mode->timing.h_back_porch);
+ dp_mode->timing.h_front_porch = drm_mode->hsync_start -
+ drm_mode->hdisplay;
+ dp_mode->timing.h_skew = drm_mode->hskew;
+
+ dp_mode->timing.v_active = drm_mode->vdisplay;
+ dp_mode->timing.v_back_porch = drm_mode->vtotal - drm_mode->vsync_end;
+ dp_mode->timing.v_sync_width = drm_mode->vtotal -
+ (drm_mode->vsync_start + dp_mode->timing.v_back_porch);
+
+ dp_mode->timing.v_front_porch = drm_mode->vsync_start -
+ drm_mode->vdisplay;
+
+ dp_mode->timing.refresh_rate = drm_mode->vrefresh;
+
+ dp_mode->timing.pixel_clk_khz = drm_mode->clock;
+
+ dp_mode->timing.v_active_low =
+ !!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC);
+
+ dp_mode->timing.h_active_low =
+ !!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
+}
+
+static void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
+ struct drm_display_mode *drm_mode)
+{
+ u32 flags = 0;
+
+ memset(drm_mode, 0, sizeof(*drm_mode));
+
+ drm_mode->hdisplay = dp_mode->timing.h_active;
+ drm_mode->hsync_start = drm_mode->hdisplay +
+ dp_mode->timing.h_front_porch;
+ drm_mode->hsync_end = drm_mode->hsync_start +
+ dp_mode->timing.h_sync_width;
+ drm_mode->htotal = drm_mode->hsync_end + dp_mode->timing.h_back_porch;
+ drm_mode->hskew = dp_mode->timing.h_skew;
+
+ drm_mode->vdisplay = dp_mode->timing.v_active;
+ drm_mode->vsync_start = drm_mode->vdisplay +
+ dp_mode->timing.v_front_porch;
+ drm_mode->vsync_end = drm_mode->vsync_start +
+ dp_mode->timing.v_sync_width;
+ drm_mode->vtotal = drm_mode->vsync_end + dp_mode->timing.v_back_porch;
+
+ drm_mode->vrefresh = dp_mode->timing.refresh_rate;
+ drm_mode->clock = dp_mode->timing.pixel_clk_khz;
+
+ if (dp_mode->timing.h_active_low)
+ flags |= DRM_MODE_FLAG_NHSYNC;
+ else
+ flags |= DRM_MODE_FLAG_PHSYNC;
+
+ if (dp_mode->timing.v_active_low)
+ flags |= DRM_MODE_FLAG_NVSYNC;
+ else
+ flags |= DRM_MODE_FLAG_PVSYNC;
+
+ drm_mode->flags = flags;
+
+ drm_mode->type = 0x48;
+ drm_mode_set_name(drm_mode);
+}
+
+static int dp_bridge_attach(struct drm_bridge *dp_bridge)
+{
+ struct dp_bridge *bridge = to_dp_bridge(dp_bridge);
+
+ if (!dp_bridge) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ pr_debug("[%d] attached\n", bridge->id);
+
+ return 0;
+}
+
+static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
+{
+ int rc = 0;
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ /* By this point mode should have been validated through mode_fixup */
+ rc = dp->set_mode(dp, &bridge->dp_mode);
+ if (rc) {
+ pr_err("[%d] failed to perform a mode set, rc=%d\n",
+ bridge->id, rc);
+ return;
+ }
+
+ rc = dp->prepare(dp);
+ if (rc) {
+ pr_err("[%d] DP display prepare failed, rc=%d\n",
+ bridge->id, rc);
+ return;
+ }
+
+ rc = dp->enable(dp);
+ if (rc) {
+ pr_err("[%d] DP display enable failed, rc=%d\n",
+ bridge->id, rc);
+ dp->unprepare(dp);
+ }
+}
+
+static void dp_bridge_enable(struct drm_bridge *drm_bridge)
+{
+ int rc = 0;
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ rc = dp->post_enable(dp);
+ if (rc)
+ pr_err("[%d] DP display post enable failed, rc=%d\n",
+ bridge->id, rc);
+}
+
+static void dp_bridge_disable(struct drm_bridge *drm_bridge)
+{
+ int rc = 0;
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ rc = dp->pre_disable(dp);
+ if (rc) {
+ pr_err("[%d] DP display pre disable failed, rc=%d\n",
+ bridge->id, rc);
+ }
+}
+
+static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
+{
+ int rc = 0;
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ rc = dp->disable(dp);
+ if (rc) {
+ pr_err("[%d] DP display disable failed, rc=%d\n",
+ bridge->id, rc);
+ return;
+ }
+
+ rc = dp->unprepare(dp);
+ if (rc) {
+ pr_err("[%d] DP display unprepare failed, rc=%d\n",
+ bridge->id, rc);
+ return;
+ }
+}
+
+static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge || !mode || !adjusted_mode) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ memset(&bridge->dp_mode, 0x0, sizeof(struct dp_display_mode));
+ convert_to_dp_mode(adjusted_mode, &bridge->dp_mode);
+}
+
+static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ int rc = 0;
+ bool ret = true;
+ struct dp_display_mode dp_mode;
+ struct dp_bridge *bridge;
+ struct dp_display *dp;
+
+ if (!drm_bridge || !mode || !adjusted_mode) {
+ pr_err("Invalid params\n");
+ ret = false;
+ goto end;
+ }
+
+ bridge = to_dp_bridge(drm_bridge);
+ dp = bridge->display;
+
+ convert_to_dp_mode(mode, &dp_mode);
+
+ rc = dp->validate_mode(dp, &dp_mode);
+ if (rc) {
+ pr_err("[%d] mode is not valid, rc=%d\n", bridge->id, rc);
+ ret = false;
+ } else {
+ convert_to_drm_mode(&dp_mode, adjusted_mode);
+ }
+end:
+ return ret;
+}
+
+static const struct drm_bridge_funcs dp_bridge_ops = {
+ .attach = dp_bridge_attach,
+ .mode_fixup = dp_bridge_mode_fixup,
+ .pre_enable = dp_bridge_pre_enable,
+ .enable = dp_bridge_enable,
+ .disable = dp_bridge_disable,
+ .post_disable = dp_bridge_post_disable,
+ .mode_set = dp_bridge_mode_set,
+};
+
+int dp_connector_post_init(struct drm_connector *connector,
+ void *info,
+ void *display)
+{
+ struct dp_display *dp_display = display;
+
+ if (!info || !dp_display)
+ return -EINVAL;
+
+ return 0;
+}
+
+int dp_connector_get_info(struct msm_display_info *info, void *data)
+{
+ struct dsi_display *display = data;
+
+ if (!info || !display) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ info->intf_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+ info->num_of_h_tiles = 1;
+ info->h_tile_instance[0] = 0;
+
+ info->is_connected = true;
+ info->frame_rate = 60;
+ info->width_mm = 160;
+ info->height_mm = 90;
+ info->max_width = 1920;
+ info->max_height = 1080;
+ info->vtotal = 1125;
+ info->is_primary = true;
+ info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE;
+ info->capabilities |= MSM_DISPLAY_CAP_VID_MODE;
+
+ return 0;
+}
+
+enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
+ bool force,
+ void *display)
+{
+ enum drm_connector_status status = connector_status_unknown;
+ struct msm_display_info info;
+ int rc;
+
+ if (!conn || !display)
+ return status;
+
+ /* get display dp_info */
+ memset(&info, 0x0, sizeof(info));
+ rc = dp_connector_get_info(&info, display);
+ if (rc) {
+ pr_err("failed to get display info, rc=%d\n", rc);
+ return connector_status_disconnected;
+ }
+
+ if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
+ status = (info.is_connected ? connector_status_connected :
+ connector_status_disconnected);
+ else
+ status = connector_status_connected;
+
+ conn->display_info.width_mm = info.width_mm;
+ conn->display_info.height_mm = info.height_mm;
+
+ return status;
+}
+
+int dp_connector_get_modes(struct drm_connector *connector,
+ void *display)
+{
+ u32 count = 0;
+ u32 size = 0;
+ struct dp_display_mode *modes;
+ struct drm_display_mode drm_mode;
+ struct dp_display *dp;
+ int rc, i;
+
+ if (!connector || !display || sde_connector_get_panel(connector))
+ goto end;
+
+ dp = display;
+
+ rc = dp->get_modes(dp, NULL, &count);
+ if (rc) {
+ pr_err("failed to get num of modes, rc=%d\n", rc);
+ goto end;
+ }
+
+ size = count * sizeof(*modes);
+ modes = kzalloc(size, GFP_KERNEL);
+ if (!modes) {
+ count = 0;
+ goto end;
+ }
+
+ rc = dp->get_modes(dp, modes, &count);
+ if (rc) {
+ pr_err("failed to get modes, rc=%d\n", rc);
+ count = 0;
+ goto error;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct drm_display_mode *m;
+
+ memset(&drm_mode, 0x0, sizeof(drm_mode));
+ convert_to_drm_mode(&modes[i], &drm_mode);
+ m = drm_mode_duplicate(connector->dev, &drm_mode);
+ if (!m) {
+ pr_err("failed to add mode %ux%u\n",
+ drm_mode.hdisplay,
+ drm_mode.vdisplay);
+ count = -ENOMEM;
+ goto error;
+ }
+ m->width_mm = connector->display_info.width_mm;
+ m->height_mm = connector->display_info.height_mm;
+ drm_mode_probed_add(connector, m);
+ }
+error:
+ kfree(modes);
+end:
+ pr_debug("MODE COUNT =%d\n\n", count);
+ return count;
+}
+
+int dp_drm_bridge_init(void *data, struct drm_encoder *encoder)
+{
+ int rc = 0;
+ struct dp_bridge *bridge;
+ struct drm_device *dev;
+ struct dp_display *display = data;
+ struct msm_drm_private *priv = NULL;
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ dev = display->drm_dev;
+ bridge->display = display;
+ bridge->base.funcs = &dp_bridge_ops;
+ bridge->base.encoder = encoder;
+
+ priv = dev->dev_private;
+
+ rc = drm_bridge_attach(dev, &bridge->base);
+ if (rc) {
+ pr_err("failed to attach bridge, rc=%d\n", rc);
+ goto error_free_bridge;
+ }
+
+ encoder->bridge = &bridge->base;
+ priv->bridges[priv->num_bridges++] = &bridge->base;
+ display->bridge = bridge;
+
+ return 0;
+error_free_bridge:
+ kfree(bridge);
+error:
+ return rc;
+}
+
+void dp_drm_bridge_deinit(void *data)
+{
+ struct dp_display *display = data;
+ struct dp_bridge *bridge = display->bridge;
+
+ if (bridge && bridge->base.encoder)
+ bridge->base.encoder->bridge = NULL;
+
+ kfree(bridge);
+}
+
+enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ void *display)
+{
+ return MODE_OK;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
new file mode 100644
index 0000000..4ff1e07
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _DP_DRM_H_
+#define _DP_DRM_H_
+
+#include <linux/types.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "msm_drv.h"
+#include "dp_display.h"
+
+struct dp_bridge {
+ struct drm_bridge base;
+ u32 id;
+
+ struct dp_display *display;
+ struct dp_display_mode dp_mode;
+};
+
+/**
+ * dp_connector_post_init - callback to perform additional initialization steps
+ * @connector: Pointer to drm connector structure
+ * @info: Pointer to sde connector info structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+int dp_connector_post_init(struct drm_connector *connector,
+ void *info,
+ void *display);
+
+/**
+ * dp_connector_detect - callback to determine if connector is connected
+ * @connector: Pointer to drm connector structure
+ * @force: Force detect setting from drm framework
+ * @display: Pointer to private display handle
+ * Returns: Connector 'is connected' status
+ */
+enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
+ bool force,
+ void *display);
+
+/**
+ * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Number of modes added
+ */
+int dp_connector_get_modes(struct drm_connector *connector,
+ void *display);
+
+/**
+ * dp_connector_mode_valid - callback to determine if specified mode is valid
+ * @connector: Pointer to drm connector structure
+ * @mode: Pointer to drm mode structure
+ * @display: Pointer to private display handle
+ * Returns: Validity status for specified mode
+ */
+enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ void *display);
+
+int dp_connector_get_info(struct msm_display_info *info, void *display);
+
+int dp_drm_bridge_init(void *display,
+ struct drm_encoder *encoder);
+
+void dp_drm_bridge_deinit(void *display);
+#endif /* _DP_DRM_H_ */
+
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
new file mode 100644
index 0000000..e9955a9
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -0,0 +1,1809 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include "dp_link.h"
+#include "dp_panel.h"
+
+#define DP_LINK_ENUM_STR(x) #x
+
+enum dp_lane_count {
+ DP_LANE_COUNT_1 = 1,
+ DP_LANE_COUNT_2 = 2,
+ DP_LANE_COUNT_4 = 4,
+};
+
+enum phy_test_pattern {
+ PHY_TEST_PATTERN_NONE,
+ PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING,
+ PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT,
+ PHY_TEST_PATTERN_PRBS7,
+ PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN,
+ PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN,
+};
+
+enum dynamic_range {
+ DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
+ DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
+ DP_DYNAMIC_RANGE_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum test_video_pattern {
+ DP_TEST_VIDEO_PATTERN_NONE = 0x00,
+ DP_TEST_VIDEO_PATTERN_COLOR_RAMPS = 0x01,
+ DP_TEST_VIDEO_PATTERN_BW_VERT_LINES = 0x02,
+ DP_TEST_VIDEO_PATTERN_COLOR_SQUARE = 0x03,
+};
+
+enum test_bit_depth {
+ DP_TEST_BIT_DEPTH_6 = 0x00,
+ DP_TEST_BIT_DEPTH_8 = 0x01,
+ DP_TEST_BIT_DEPTH_10 = 0x02,
+ DP_TEST_BIT_DEPTH_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum dp_link_response {
+ TEST_ACK = 0x1,
+ TEST_NACK = 0x2,
+ TEST_EDID_CHECKSUM_WRITE = 0x4,
+};
+
+enum audio_sample_rate {
+ AUDIO_SAMPLE_RATE_32_KHZ = 0x00,
+ AUDIO_SAMPLE_RATE_44_1_KHZ = 0x01,
+ AUDIO_SAMPLE_RATE_48_KHZ = 0x02,
+ AUDIO_SAMPLE_RATE_88_2_KHZ = 0x03,
+ AUDIO_SAMPLE_RATE_96_KHZ = 0x04,
+ AUDIO_SAMPLE_RATE_176_4_KHZ = 0x05,
+ AUDIO_SAMPLE_RATE_192_KHZ = 0x06,
+};
+
+enum audio_pattern_type {
+ AUDIO_TEST_PATTERN_OPERATOR_DEFINED = 0x00,
+ AUDIO_TEST_PATTERN_SAWTOOTH = 0x01,
+};
+
+struct dp_link_request {
+ u32 test_requested;
+ u32 test_link_rate;
+ u32 test_lane_count;
+ u32 phy_test_pattern_sel;
+ u32 test_video_pattern;
+ u32 test_bit_depth;
+ u32 test_dyn_range;
+ u32 test_h_total;
+ u32 test_v_total;
+ u32 test_h_start;
+ u32 test_v_start;
+ u32 test_hsync_pol;
+ u32 test_hsync_width;
+ u32 test_vsync_pol;
+ u32 test_vsync_width;
+ u32 test_h_width;
+ u32 test_v_height;
+ u32 test_rr_d;
+ u32 test_rr_n;
+ u32 test_audio_sampling_rate;
+ u32 test_audio_channel_count;
+ u32 test_audio_pattern_type;
+ u32 test_audio_period_ch_1;
+ u32 test_audio_period_ch_2;
+ u32 test_audio_period_ch_3;
+ u32 test_audio_period_ch_4;
+ u32 test_audio_period_ch_5;
+ u32 test_audio_period_ch_6;
+ u32 test_audio_period_ch_7;
+ u32 test_audio_period_ch_8;
+ u32 response;
+};
+
+struct dp_link_sink_count {
+ u32 count;
+ bool cp_ready;
+};
+
+struct dp_link_status {
+ u8 lane_01_status;
+ u8 lane_23_status;
+ u8 interlane_align_done;
+ u8 downstream_port_status_changed;
+ u8 link_status_updated;
+ u8 port_0_in_sync;
+ u8 port_1_in_sync;
+ u8 req_voltage_swing[4];
+ u8 req_pre_emphasis[4];
+};
+
+struct dp_link_private {
+ struct device *dev;
+ struct dp_aux *aux;
+ struct dp_link dp_link;
+
+ struct dp_link_request request;
+ struct dp_link_sink_count sink_count;
+ struct dp_link_status link_status;
+};
+
+/**
+ * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
+ * @tbd: test bit depth
+ *
+ * Returns the bits per pixel (bpp) to be used corresponding to the
+ * git bit depth value. This function assumes that bit depth has
+ * already been validated.
+ */
+static inline u32 dp_link_bit_depth_to_bpp(enum test_bit_depth tbd)
+{
+ u32 bpp;
+
+ /*
+ * Few simplistic rules and assumptions made here:
+ * 1. Bit depth is per color component
+ * 2. If bit depth is unknown return 0
+ * 3. Assume 3 color components
+ */
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ bpp = 18;
+ break;
+ case DP_TEST_BIT_DEPTH_8:
+ bpp = 24;
+ break;
+ case DP_TEST_BIT_DEPTH_10:
+ bpp = 30;
+ break;
+ case DP_TEST_BIT_DEPTH_UNKNOWN:
+ default:
+ bpp = 0;
+ }
+
+ return bpp;
+}
+
+static char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_NONE);
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING);
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ return DP_LINK_ENUM_STR(
+ PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
+ case PHY_TEST_PATTERN_PRBS7:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_PRBS7);
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN);
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN);
+ default:
+ return "unknown";
+ }
+}
+
+static char *dp_link_get_audio_test_pattern(u32 pattern)
+{
+ switch (pattern) {
+ case AUDIO_TEST_PATTERN_OPERATOR_DEFINED:
+ return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_OPERATOR_DEFINED);
+ case AUDIO_TEST_PATTERN_SAWTOOTH:
+ return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_SAWTOOTH);
+ default:
+ return "unknown";
+ }
+}
+
+static char *dp_link_get_audio_sample_rate(u32 rate)
+{
+ switch (rate) {
+ case AUDIO_SAMPLE_RATE_32_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_32_KHZ);
+ case AUDIO_SAMPLE_RATE_44_1_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_44_1_KHZ);
+ case AUDIO_SAMPLE_RATE_48_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_48_KHZ);
+ case AUDIO_SAMPLE_RATE_88_2_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_88_2_KHZ);
+ case AUDIO_SAMPLE_RATE_96_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_96_KHZ);
+ case AUDIO_SAMPLE_RATE_176_4_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_176_4_KHZ);
+ case AUDIO_SAMPLE_RATE_192_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_192_KHZ);
+ default:
+ return "unknown";
+ }
+}
+
+static int dp_link_get_period(struct dp_link_private *link, int const addr)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ u32 const param_len = 0x1;
+ u32 const max_audio_period = 0xA;
+
+ /* TEST_AUDIO_PERIOD_CH_XX */
+ rlen = link->aux->read(link->aux, addr, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read test_audio_period (0x%x)\n", addr);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ data = *bp;
+
+ /* Period - Bits 3:0 */
+ data = data & 0xF;
+ if ((int)data > max_audio_period) {
+ pr_err("invalid test_audio_period_ch_1 = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = data;
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
+{
+ int ret = 0;
+ int const test_audio_period_ch_1_addr = 0x273;
+ int const test_audio_period_ch_2_addr = 0x274;
+ int const test_audio_period_ch_3_addr = 0x275;
+ int const test_audio_period_ch_4_addr = 0x276;
+ int const test_audio_period_ch_5_addr = 0x277;
+ int const test_audio_period_ch_6_addr = 0x278;
+ int const test_audio_period_ch_7_addr = 0x279;
+ int const test_audio_period_ch_8_addr = 0x27A;
+ struct dp_link_request *req = &link->request;
+
+ /* TEST_AUDIO_PERIOD_CH_1 (Byte 0x273) */
+ ret = dp_link_get_period(link, test_audio_period_ch_1_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_1 = ret;
+ pr_debug("test_audio_period_ch_1 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_2 (Byte 0x274) */
+ ret = dp_link_get_period(link, test_audio_period_ch_2_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_2 = ret;
+ pr_debug("test_audio_period_ch_2 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
+ ret = dp_link_get_period(link, test_audio_period_ch_3_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_3 = ret;
+ pr_debug("test_audio_period_ch_3 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_4 (Byte 0x276) */
+ ret = dp_link_get_period(link, test_audio_period_ch_4_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_4 = ret;
+ pr_debug("test_audio_period_ch_4 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_5 (Byte 0x277) */
+ ret = dp_link_get_period(link, test_audio_period_ch_5_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_5 = ret;
+ pr_debug("test_audio_period_ch_5 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_6 (Byte 0x278) */
+ ret = dp_link_get_period(link, test_audio_period_ch_6_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_6 = ret;
+ pr_debug("test_audio_period_ch_6 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_7 (Byte 0x279) */
+ ret = dp_link_get_period(link, test_audio_period_ch_7_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_7 = ret;
+ pr_debug("test_audio_period_ch_7 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_8 (Byte 0x27A) */
+ ret = dp_link_get_period(link, test_audio_period_ch_8_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_8 = ret;
+ pr_debug("test_audio_period_ch_8 = 0x%x\n", ret);
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_audio_pattern_type_addr = 0x272;
+ int const max_audio_pattern_type = 0x1;
+
+ /* Read the requested audio pattern type (Byte 0x272). */
+ rlen = link->aux->read(link->aux, test_audio_pattern_type_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link audio mode data\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Audio Pattern Type - Bits 7:0 */
+ if ((int)data > max_audio_pattern_type) {
+ pr_err("invalid audio pattern type = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_audio_pattern_type = data;
+ pr_debug("audio pattern type = %s\n",
+ dp_link_get_audio_test_pattern(data));
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_mode(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_audio_mode_addr = 0x271;
+ int const max_audio_sampling_rate = 0x6;
+ int const max_audio_channel_count = 0x8;
+ int sampling_rate = 0x0;
+ int channel_count = 0x0;
+
+ /* Read the requested audio mode (Byte 0x271). */
+ rlen = link->aux->read(link->aux, test_audio_mode_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link audio mode data\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Sampling Rate - Bits 3:0 */
+ sampling_rate = data & 0xF;
+ if (sampling_rate > max_audio_sampling_rate) {
+ pr_err("sampling rate (0x%x) greater than max (0x%x)\n",
+ sampling_rate, max_audio_sampling_rate);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Channel Count - Bits 7:4 */
+ channel_count = ((data & 0xF0) >> 4) + 1;
+ if (channel_count > max_audio_channel_count) {
+ pr_err("channel_count (0x%x) greater than max (0x%x)\n",
+ channel_count, max_audio_channel_count);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_audio_sampling_rate = sampling_rate;
+ link->request.test_audio_channel_count = channel_count;
+ pr_debug("sampling_rate = %s, channel_count = 0x%x\n",
+ dp_link_get_audio_sample_rate(sampling_rate), channel_count);
+exit:
+ return ret;
+}
+
+/**
+ * dp_parse_audio_pattern_params() - parses audio pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the audio link pattern parameters.
+ */
+static int dp_link_parse_audio_pattern_params(struct dp_link_private *link)
+{
+ int ret = 0;
+
+ ret = dp_link_parse_audio_mode(link);
+ if (ret)
+ goto exit;
+
+ ret = dp_link_parse_audio_pattern_type(link);
+ if (ret)
+ goto exit;
+
+ ret = dp_link_parse_audio_channel_period(link);
+
+exit:
+ return ret;
+}
+
+/**
+ * dp_link_is_video_pattern_valid() - validates the video pattern
+ * @pattern: video pattern requested by the sink
+ *
+ * Returns true if the requested video pattern is supported.
+ */
+static bool dp_link_is_video_pattern_valid(u32 pattern)
+{
+ switch (pattern) {
+ case DP_TEST_VIDEO_PATTERN_NONE:
+ case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
+ case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
+ case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_video_pattern_to_string(u32 test_video_pattern)
+{
+ switch (test_video_pattern) {
+ case DP_TEST_VIDEO_PATTERN_NONE:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_NONE);
+ case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_RAMPS);
+ case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_BW_VERT_LINES);
+ case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_SQUARE);
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_dynamic_range_valid() - validates the dynamic range
+ * @bit_depth: the dynamic range value to be checked
+ *
+ * Returns true if the dynamic range value is supported.
+ */
+static bool dp_link_is_dynamic_range_valid(u32 dr)
+{
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_dynamic_range_to_string(u32 dr)
+{
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_VESA);
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_CEA);
+ case DP_DYNAMIC_RANGE_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_bit_depth_valid() - validates the bit depth requested
+ * @bit_depth: bit depth requested by the sink
+ *
+ * Returns true if the requested bit depth is supported.
+ */
+static bool dp_link_is_bit_depth_valid(u32 tbd)
+{
+ /* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ case DP_TEST_BIT_DEPTH_8:
+ case DP_TEST_BIT_DEPTH_10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_bit_depth_to_string(u32 tbd)
+{
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_6);
+ case DP_TEST_BIT_DEPTH_8:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_8);
+ case DP_TEST_BIT_DEPTH_10:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_10);
+ case DP_TEST_BIT_DEPTH_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+static int dp_link_parse_timing_params1(struct dp_link_private *link,
+ int const addr, int const len, u32 *val)
+{
+ u8 *bp;
+ int rlen;
+
+ if (len < 2)
+ return -EINVAL;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+
+ *val = bp[1] | (bp[0] << 8);
+
+ return 0;
+}
+
+static int dp_link_parse_timing_params2(struct dp_link_private *link,
+ int const addr, int const len, u32 *val1, u32 *val2)
+{
+ u8 *bp;
+ int rlen;
+
+ if (len < 2)
+ return -EINVAL;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+
+ *val1 = (bp[0] & BIT(7)) >> 7;
+ *val2 = bp[1] | ((bp[0] & 0x7F) << 8);
+
+ return 0;
+}
+
+static int dp_link_parse_timing_params3(struct dp_link_private *link,
+ int const addr, u32 *val)
+{
+ u8 *bp;
+ u32 len = 1;
+ int rlen;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < 1) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+ *val = bp[0];
+
+ return 0;
+}
+
+/**
+ * dp_parse_video_pattern_params() - parses video pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the video link pattern and the link
+ * bit depth requested by the sink and, and if the values parsed are valid.
+ */
+static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
+{
+ int ret = 0;
+ int rlen;
+ u8 *bp;
+ u8 data;
+ u32 dyn_range;
+ int const param_len = 0x1;
+ int const test_video_pattern_addr = 0x221;
+ int const test_misc_addr = 0x232;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, test_video_pattern_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link video pattern\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ if (!dp_link_is_video_pattern_valid(data)) {
+ pr_err("invalid link video pattern = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_video_pattern = data;
+ pr_debug("link video pattern = 0x%x (%s)\n",
+ link->request.test_video_pattern,
+ dp_link_video_pattern_to_string(
+ link->request.test_video_pattern));
+
+ /* Read the requested color bit depth and dynamic range (Byte 0x232) */
+ rlen = link->aux->read(link->aux, test_misc_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link bit depth\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Dynamic Range */
+ dyn_range = (data & BIT(3)) >> 3;
+ if (!dp_link_is_dynamic_range_valid(dyn_range)) {
+ pr_err("invalid link dynamic range = 0x%x", dyn_range);
+ ret = -EINVAL;
+ goto exit;
+ }
+ link->request.test_dyn_range = dyn_range;
+ pr_debug("link dynamic range = 0x%x (%s)\n",
+ link->request.test_dyn_range,
+ dp_link_dynamic_range_to_string(
+ link->request.test_dyn_range));
+
+ /* Color bit depth */
+ data &= (BIT(5) | BIT(6) | BIT(7));
+ data >>= 5;
+ if (!dp_link_is_bit_depth_valid(data)) {
+ pr_err("invalid link bit depth = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_bit_depth = data;
+ pr_debug("link bit depth = 0x%x (%s)\n",
+ link->request.test_bit_depth,
+ dp_link_bit_depth_to_string(link->request.test_bit_depth));
+
+ /* resolution timing params */
+ ret = dp_link_parse_timing_params1(link, 0x222, 2,
+ &link->request.test_h_total);
+ if (ret) {
+ pr_err("failed to parse test_h_total (0x222)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_TOTAL = %d\n", link->request.test_h_total);
+
+ ret = dp_link_parse_timing_params1(link, 0x224, 2,
+ &link->request.test_v_total);
+ if (ret) {
+ pr_err("failed to parse test_v_total (0x224)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_TOTAL = %d\n", link->request.test_v_total);
+
+ ret = dp_link_parse_timing_params1(link, 0x226, 2,
+ &link->request.test_h_start);
+ if (ret) {
+ pr_err("failed to parse test_h_start (0x226)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_START = %d\n", link->request.test_h_start);
+
+ ret = dp_link_parse_timing_params1(link, 0x228, 2,
+ &link->request.test_v_start);
+ if (ret) {
+ pr_err("failed to parse test_v_start (0x228)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_START = %d\n", link->request.test_v_start);
+
+ ret = dp_link_parse_timing_params2(link, 0x22A, 2,
+ &link->request.test_hsync_pol,
+ &link->request.test_hsync_width);
+ if (ret) {
+ pr_err("failed to parse (0x22A)\n");
+ goto exit;
+ }
+ pr_debug("TEST_HSYNC_POL = %d\n", link->request.test_hsync_pol);
+ pr_debug("TEST_HSYNC_WIDTH = %d\n", link->request.test_hsync_width);
+
+ ret = dp_link_parse_timing_params2(link, 0x22C, 2,
+ &link->request.test_vsync_pol,
+ &link->request.test_vsync_width);
+ if (ret) {
+ pr_err("failed to parse (0x22C)\n");
+ goto exit;
+ }
+ pr_debug("TEST_VSYNC_POL = %d\n", link->request.test_vsync_pol);
+ pr_debug("TEST_VSYNC_WIDTH = %d\n", link->request.test_vsync_width);
+
+ ret = dp_link_parse_timing_params1(link, 0x22E, 2,
+ &link->request.test_h_width);
+ if (ret) {
+ pr_err("failed to parse test_h_width (0x22E)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_WIDTH = %d\n", link->request.test_h_width);
+
+ ret = dp_link_parse_timing_params1(link, 0x230, 2,
+ &link->request.test_v_height);
+ if (ret) {
+ pr_err("failed to parse test_v_height (0x230)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_HEIGHT = %d\n", link->request.test_v_height);
+
+ ret = dp_link_parse_timing_params3(link, 0x233,
+ &link->request.test_rr_d);
+ link->request.test_rr_d &= BIT(0);
+ if (ret) {
+ pr_err("failed to parse test_rr_d (0x233)\n");
+ goto exit;
+ }
+ pr_debug("TEST_REFRESH_DENOMINATOR = %d\n", link->request.test_rr_d);
+
+ ret = dp_link_parse_timing_params3(link, 0x234,
+ &link->request.test_rr_n);
+ if (ret) {
+ pr_err("failed to parse test_rr_n (0x234)\n");
+ goto exit;
+ }
+ pr_debug("TEST_REFRESH_NUMERATOR = %d\n", link->request.test_rr_n);
+exit:
+ return ret;
+}
+
+/**
+ * dp_link_is_link_rate_valid() - validates the link rate
+ * @lane_rate: link rate requested by the sink
+ *
+ * Returns true if the requested link rate is supported.
+ */
+static bool dp_link_is_link_rate_valid(u32 link_rate)
+{
+ return ((link_rate == DP_LINK_RATE_162) ||
+ (link_rate == DP_LINK_RATE_270) ||
+ (link_rate == DP_LINK_RATE_540) ||
+ (link_rate == DP_LINK_RATE_810));
+}
+
+/**
+ * dp_link_is_lane_count_valid() - validates the lane count
+ * @lane_count: lane count requested by the sink
+ *
+ * Returns true if the requested lane count is supported.
+ */
+static bool dp_link_is_lane_count_valid(u32 lane_count)
+{
+ return (lane_count == DP_LANE_COUNT_1) ||
+ (lane_count == DP_LANE_COUNT_2) ||
+ (lane_count == DP_LANE_COUNT_4);
+}
+
+/**
+ * dp_link_parse_link_training_params() - parses link training parameters from
+ * DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane
+ * count (Byte 0x220), and if these values parse are valid.
+ */
+static int dp_link_parse_link_training_params(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int ret = 0;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_link_rate_addr = 0x219;
+ int const test_lane_count_addr = 0x220;
+
+ /* Read the requested link rate (Byte 0x219). */
+ rlen = link->aux->read(link->aux, test_link_rate_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link rate\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ if (!dp_link_is_link_rate_valid(data)) {
+ pr_err("invalid link rate = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_link_rate = data;
+ pr_debug("link rate = 0x%x\n", link->request.test_link_rate);
+
+ /* Read the requested lane count (Byte 0x220). */
+ rlen = link->aux->read(link->aux, test_lane_count_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read lane count\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+ data &= 0x1F;
+
+ if (!dp_link_is_lane_count_valid(data)) {
+ pr_err("invalid lane count = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_lane_count = data;
+ pr_debug("lane count = 0x%x\n", link->request.test_lane_count);
+exit:
+ return ret;
+}
+
+static bool dp_link_is_phy_test_pattern_supported(u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ case PHY_TEST_PATTERN_PRBS7:
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * dp_parse_phy_test_params() - parses the phy link parameters
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being
+ * requested.
+ */
+static int dp_link_parse_phy_test_params(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const phy_test_pattern_addr = 0x248;
+ int ret = 0;
+
+ rlen = link->aux->read(link->aux, phy_test_pattern_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read phy link pattern\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ link->request.phy_test_pattern_sel = data;
+
+ pr_debug("phy_test_pattern_sel = %s\n",
+ dp_link_get_phy_test_pattern(data));
+
+ if (!dp_link_is_phy_test_pattern_supported(data))
+ ret = -EINVAL;
+end:
+ return ret;
+}
+
+static char *dp_link_get_test_name(u32 test_requested)
+{
+ switch (test_requested) {
+ case TEST_LINK_TRAINING: return DP_LINK_ENUM_STR(TEST_LINK_TRAINING);
+ case TEST_VIDEO_PATTERN: return DP_LINK_ENUM_STR(TEST_VIDEO_PATTERN);
+ case PHY_TEST_PATTERN: return DP_LINK_ENUM_STR(PHY_TEST_PATTERN);
+ case TEST_EDID_READ: return DP_LINK_ENUM_STR(TEST_EDID_READ);
+ case TEST_AUDIO_PATTERN: return DP_LINK_ENUM_STR(TEST_AUDIO_PATTERN);
+ default: return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_video_audio_test_requested() - checks for audio/video link request
+ * @link: link requested by the sink
+ *
+ * Returns true if the requested link is a permitted audio/video link.
+ */
+static bool dp_link_is_video_audio_test_requested(u32 link)
+{
+ return (link == TEST_VIDEO_PATTERN) ||
+ (link == (TEST_AUDIO_PATTERN | TEST_VIDEO_PATTERN)) ||
+ (link == TEST_AUDIO_PATTERN) ||
+ (link == (TEST_AUDIO_PATTERN | TEST_AUDIO_DISABLED_VIDEO));
+}
+
+/**
+ * dp_link_supported() - checks if link requested by sink is supported
+ * @test_requested: link requested by the sink
+ *
+ * Returns true if the requested link is supported.
+ */
+static bool dp_link_is_test_supported(u32 test_requested)
+{
+ return (test_requested == TEST_LINK_TRAINING) ||
+ (test_requested == TEST_EDID_READ) ||
+ (test_requested == PHY_TEST_PATTERN) ||
+ dp_link_is_video_audio_test_requested(test_requested);
+}
+
+/**
+ * dp_sink_parse_test_request() - parses link request parameters from sink
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD to check if an automated link is requested (Byte 0x201),
+ * and what type of link automation is being requested (Byte 0x218).
+ */
+static int dp_link_parse_request(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ u32 const param_len = 0x1;
+ u32 const device_service_irq_addr = 0x201;
+ u32 const test_request_addr = 0x218;
+ u8 buf[4];
+
+ /**
+ * Read the device service IRQ vector (Byte 0x201) to determine
+ * whether an automated link has been requested by the sink.
+ */
+ rlen = link->aux->read(link->aux, device_service_irq_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("aux read failed\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("device service irq vector = 0x%x\n", data);
+
+ if (!(data & BIT(1))) {
+ pr_debug("no link requested\n");
+ goto end;
+ }
+
+ /**
+ * Read the link request byte (Byte 0x218) to determine what type
+ * of automated link has been requested by the sink.
+ */
+ rlen = link->aux->read(link->aux, test_request_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("aux read failed\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ if (!dp_link_is_test_supported(data)) {
+ pr_debug("link 0x%x not supported\n", data);
+ goto end;
+ }
+
+ pr_debug("%s (0x%x) requested\n", dp_link_get_test_name(data), data);
+ link->request.test_requested = data;
+
+ if (link->request.test_requested == PHY_TEST_PATTERN) {
+ ret = dp_link_parse_phy_test_params(link);
+ if (ret)
+ goto end;
+ ret = dp_link_parse_link_training_params(link);
+ }
+
+ if (link->request.test_requested == TEST_LINK_TRAINING)
+ ret = dp_link_parse_link_training_params(link);
+
+ if (dp_link_is_video_audio_test_requested(
+ link->request.test_requested)) {
+ ret = dp_link_parse_video_pattern_params(link);
+ if (ret)
+ goto end;
+
+ ret = dp_link_parse_audio_pattern_params(link);
+ }
+end:
+ /* clear the link request IRQ */
+ buf[0] = 1;
+ link->aux->write(link->aux, test_request_addr, 1, AUX_NATIVE, buf);
+
+ /**
+ * Send a TEST_ACK if all link parameters are valid, otherwise send
+ * a TEST_NACK.
+ */
+ if (ret)
+ link->request.response = TEST_NACK;
+ else
+ link->request.response = TEST_ACK;
+
+ return ret;
+}
+
+/**
+ * dp_link_parse_sink_count() - parses the sink count
+ *
+ * Parses the DPCD to check if there is an update to the sink count
+ * (Byte 0x200), and whether all the sink devices connected have Content
+ * Protection enabled.
+ */
+static void dp_link_parse_sink_count(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const sink_count_addr = 0x200;
+
+ rlen = link->aux->read(link->aux, sink_count_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read sink count\n");
+ return;
+ }
+
+ data = *bp;
+
+ /* BIT 7, BIT 5:0 */
+ link->sink_count.count = (data & BIT(7)) << 6 | (data & 0x63);
+ /* BIT 6*/
+ link->sink_count.cp_ready = data & BIT(6);
+
+ pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n",
+ link->sink_count.count, link->sink_count.cp_ready);
+}
+
+static int dp_link_link_status_read(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen, ret = 0;
+ int const addr = 0x202;
+ int const len = 6;
+ struct dp_link_status *sp;
+
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("edp aux read failed\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ sp = &link->link_status;
+
+ data = *bp++; /* byte 0x202 */
+ sp->lane_01_status = data; /* lane 0, 1 */
+
+ data = *bp++; /* byte 0x203 */
+ sp->lane_23_status = data; /* lane 2, 3 */
+
+ data = *bp++; /* byte 0x204 */
+ sp->interlane_align_done = (data & BIT(0));
+ sp->downstream_port_status_changed = (data & BIT(6));
+ sp->link_status_updated = (data & BIT(7));
+
+ data = *bp++; /* byte 0x205 */
+ sp->port_0_in_sync = (data & BIT(0));
+ sp->port_1_in_sync = (data & BIT(1));
+
+ data = *bp++; /* byte 0x206 */
+ sp->req_voltage_swing[0] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[0] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[1] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[1] = data & 0x03;
+
+ data = *bp++; /* byte 0x207 */
+ sp->req_voltage_swing[2] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[2] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[3] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[3] = data & 0x03;
+
+ return 0;
+error:
+ return ret;
+}
+
+static void dp_link_parse_sink_status_field(struct dp_link_private *link)
+{
+ dp_link_parse_sink_count(link);
+ dp_link_parse_request(link);
+ dp_link_link_status_read(link);
+}
+
+static bool dp_link_is_link_training_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested == TEST_LINK_TRAINING);
+}
+
+/**
+ * dp_link_process_link_training_request() - processes new training requests
+ * @link: Display Port link data
+ *
+ * This function will handle new link training requests that are initiated by
+ * the sink. In particular, it will update the requested lane count and link
+ * link rate, and then trigger the link retraining procedure.
+ *
+ * The function will return 0 if a link training request has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_training_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_link_training_requested(link))
+ return -EINVAL;
+
+ pr_debug("%s link rate = 0x%x, lane count = 0x%x\n",
+ dp_link_get_test_name(TEST_LINK_TRAINING),
+ link->request.test_link_rate,
+ link->request.test_lane_count);
+
+ link->dp_link.lane_count = link->request.test_lane_count;
+ link->dp_link.link_rate = link->request.test_link_rate;
+
+ return 0;
+}
+
+static bool dp_link_phy_pattern_requested(struct dp_link *dp_link)
+{
+ struct dp_link_private *link = container_of(dp_link,
+ struct dp_link_private, dp_link);
+
+ return (link->request.test_requested == PHY_TEST_PATTERN);
+}
+
+static int dp_link_parse_vx_px(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int const param_len = 0x1;
+ int const addr1 = 0x206;
+ int const addr2 = 0x207;
+ int ret = 0;
+ u32 v0, p0, v1, p1, v2, p2, v3, p3;
+ int rlen;
+
+ pr_debug("\n");
+
+ rlen = link->aux->read(link->aux, addr1, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed reading lanes 0/1\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("lanes 0/1 (Byte 0x206): 0x%x\n", data);
+
+ v0 = data & 0x3;
+ data = data >> 2;
+ p0 = data & 0x3;
+ data = data >> 2;
+
+ v1 = data & 0x3;
+ data = data >> 2;
+ p1 = data & 0x3;
+ data = data >> 2;
+
+ rlen = link->aux->read(link->aux, addr2, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed reading lanes 2/3\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("lanes 2/3 (Byte 0x207): 0x%x\n", data);
+
+ v2 = data & 0x3;
+ data = data >> 2;
+ p2 = data & 0x3;
+ data = data >> 2;
+
+ v3 = data & 0x3;
+ data = data >> 2;
+ p3 = data & 0x3;
+ data = data >> 2;
+
+ pr_debug("vx: 0=%d, 1=%d, 2=%d, 3=%d\n", v0, v1, v2, v3);
+ pr_debug("px: 0=%d, 1=%d, 2=%d, 3=%d\n", p0, p1, p2, p3);
+
+ /**
+ * Update the voltage and pre-emphasis levels as per DPCD request
+ * vector.
+ */
+ pr_debug("Current: v_level = 0x%x, p_level = 0x%x\n",
+ link->dp_link.v_level, link->dp_link.p_level);
+ pr_debug("Requested: v_level = 0x%x, p_level = 0x%x\n", v0, p0);
+ link->dp_link.v_level = v0;
+ link->dp_link.p_level = p0;
+
+ pr_debug("Success\n");
+end:
+ return ret;
+}
+
+/**
+ * dp_link_process_phy_test_pattern_request() - process new phy link requests
+ * @link: Display Port Driver data
+ *
+ * This function will handle new phy link pattern requests that are initiated
+ * by the sink. The function will return 0 if a phy link pattern has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_phy_test_pattern_request(
+ struct dp_link_private *link)
+{
+ u32 test_link_rate = 0, test_lane_count = 0;
+
+ if (!dp_link_phy_pattern_requested(&link->dp_link))
+ return -EINVAL;
+
+ test_link_rate = link->request.test_link_rate;
+ test_lane_count = link->request.test_lane_count;
+
+ if (!dp_link_is_link_rate_valid(test_link_rate) ||
+ !dp_link_is_lane_count_valid(test_lane_count)) {
+ pr_err("Invalid params: link rate = 0x%x, lane count = 0x%x\n",
+ test_link_rate, test_lane_count);
+ return -EINVAL;
+ }
+
+ pr_debug("start\n");
+
+ link->dp_link.lane_count = link->request.test_lane_count;
+ link->dp_link.link_rate = link->request.test_link_rate;
+
+ dp_link_parse_vx_px(link);
+
+ pr_debug("end\n");
+
+ return 0;
+}
+
+static bool dp_link_is_link_status_updated(struct dp_link_private *link)
+{
+ return link->link_status.link_status_updated;
+}
+
+static bool dp_link_channel_eq_done(struct dp_link_private *link)
+{
+ u32 mask, data;
+ struct dp_link *dp_link = &link->dp_link;
+
+ pr_debug("\n");
+
+ dp_link_link_status_read(link);
+
+ if (!link->link_status.interlane_align_done) { /* not align */
+ pr_err("interlane align failed\n");
+ return 0;
+ }
+
+ if (dp_link->lane_count == 1) {
+ mask = 0x7;
+ data = link->link_status.lane_01_status;
+ } else if (dp_link->lane_count == 2) {
+ mask = 0x77;
+ data = link->link_status.lane_01_status;
+ } else {
+ mask = 0x7777;
+ data = link->link_status.lane_23_status;
+ data <<= 8;
+ data |= link->link_status.lane_01_status;
+ }
+
+ data &= mask;
+ pr_debug("data=%x mask=%x\n", data, mask);
+
+ if (data == mask)/* all done */
+ return true;
+
+ return false;
+}
+
+static bool dp_link_clock_recovery_done(struct dp_link_private *link)
+{
+ u32 mask, data;
+ struct dp_link *dp_link = &link->dp_link;
+
+ dp_link_link_status_read(link);
+
+ if (dp_link->lane_count == 1) {
+ mask = 0x01; /* lane 0 */
+ data = link->link_status.lane_01_status;
+ } else if (dp_link->lane_count == 2) {
+ mask = 0x011; /*B lane 0, 1 */
+ data = link->link_status.lane_01_status;
+ } else {
+ mask = 0x01111; /*B lane 0, 1 */
+ data = link->link_status.lane_23_status;
+ data <<= 8;
+ data |= link->link_status.lane_01_status;
+ }
+
+ data &= mask;
+ pr_debug("data=%x mask=%x\n", data, mask);
+
+ if (data == mask) /* all done */
+ return true;
+
+ return false;
+}
+
+/**
+ * dp_link_process_link_status_update() - processes link status updates
+ * @link: Display Port link module data
+ *
+ * This function will check for changes in the link status, e.g. clock
+ * recovery done on all lanes, and trigger link training if there is a
+ * failure/error on the link.
+ *
+ * The function will return 0 if the a link status update has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_status_update(struct dp_link_private *link)
+{
+ if (!dp_link_is_link_status_updated(link) ||
+ (dp_link_channel_eq_done(link) &&
+ dp_link_clock_recovery_done(link)))
+ return -EINVAL;
+
+ pr_debug("channel_eq_done = %d, clock_recovery_done = %d\n",
+ dp_link_channel_eq_done(link),
+ dp_link_clock_recovery_done(link));
+
+ return 0;
+}
+
+static bool dp_link_is_ds_port_status_changed(struct dp_link_private *link)
+{
+ return link->link_status.downstream_port_status_changed;
+}
+
+/**
+ * dp_link_process_downstream_port_status_change() - process port status changes
+ * @link: Display Port Driver data
+ *
+ * This function will handle downstream port updates that are initiated by
+ * the sink. If the downstream port status has changed, the EDID is read via
+ * AUX.
+ *
+ * The function will return 0 if a downstream port update has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
+{
+ if (!dp_link_is_ds_port_status_changed(link))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool dp_link_is_video_pattern_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested & TEST_VIDEO_PATTERN)
+ && !(link->request.test_requested & TEST_AUDIO_DISABLED_VIDEO);
+}
+
+static bool dp_link_is_audio_pattern_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested & TEST_AUDIO_PATTERN);
+}
+
+/**
+ * dp_link_process_video_pattern_request() - process new video pattern request
+ * @link: Display Port link module's data
+ *
+ * This function will handle a new video pattern request that are initiated by
+ * the sink. This is acheieved by first sending a disconnect notification to
+ * the sink followed by a subsequent connect notification to the user modules,
+ * where it is expected that the user modules would draw the required link
+ * pattern.
+ */
+static int dp_link_process_video_pattern_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_video_pattern_requested(link))
+ goto end;
+
+ pr_debug("%s: bit depth=%d(%d bpp) pattern=%s\n",
+ dp_link_get_test_name(TEST_VIDEO_PATTERN),
+ link->request.test_bit_depth,
+ dp_link_bit_depth_to_bpp(link->request.test_bit_depth),
+ dp_link_video_pattern_to_string(
+ link->request.test_video_pattern));
+
+ return 0;
+end:
+ return -EINVAL;
+}
+
+/**
+ * dp_link_process_audio_pattern_request() - process new audio pattern request
+ * @link: Display Port link module data
+ *
+ * This function will handle a new audio pattern request that is initiated by
+ * the sink. This is acheieved by sending the necessary secondary data packets
+ * to the sink. It is expected that any simulatenous requests for video
+ * patterns will be handled before the audio pattern is sent to the sink.
+ */
+static int dp_link_process_audio_pattern_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_audio_pattern_requested(link))
+ return -EINVAL;
+
+ pr_debug("sampling_rate=%s, channel_count=%d, pattern_type=%s\n",
+ dp_link_get_audio_sample_rate(
+ link->request.test_audio_sampling_rate),
+ link->request.test_audio_channel_count,
+ dp_link_get_audio_test_pattern(
+ link->request.test_audio_pattern_type));
+
+ pr_debug("audio_period: ch1=0x%x, ch2=0x%x, ch3=0x%x, ch4=0x%x\n",
+ link->request.test_audio_period_ch_1,
+ link->request.test_audio_period_ch_2,
+ link->request.test_audio_period_ch_3,
+ link->request.test_audio_period_ch_4);
+
+ pr_debug("audio_period: ch5=0x%x, ch6=0x%x, ch7=0x%x, ch8=0x%x\n",
+ link->request.test_audio_period_ch_5,
+ link->request.test_audio_period_ch_6,
+ link->request.test_audio_period_ch_7,
+ link->request.test_audio_period_ch_8);
+
+ return 0;
+}
+
+static void dp_link_reset_data(struct dp_link_private *link)
+{
+ link->request = (const struct dp_link_request){ 0 };
+ link->request.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
+
+ link->dp_link.test_requested = 0;
+}
+
+/**
+ * dp_link_process_request() - handle HPD IRQ transition to HIGH
+ * @link: pointer to link module data
+ *
+ * This function will handle the HPD IRQ state transitions from LOW to HIGH
+ * (including cases when there are back to back HPD IRQ HIGH) indicating
+ * the start of a new link training request or sink status update.
+ */
+static int dp_link_process_request(struct dp_link *dp_link)
+{
+ int ret = 0;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ pr_debug("start\n");
+
+ dp_link_reset_data(link);
+
+ dp_link_parse_sink_status_field(link);
+
+ ret = dp_link_process_link_training_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_LINK_TRAINING;
+ goto exit;
+ }
+
+ ret = dp_link_process_phy_test_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= PHY_TEST_PATTERN;
+ goto exit;
+ }
+
+ ret = dp_link_process_link_status_update(link);
+ if (!ret) {
+ dp_link->test_requested |= LINK_STATUS_UPDATED;
+ goto exit;
+ }
+
+ ret = dp_link_process_ds_port_status_change(link);
+ if (!ret) {
+ dp_link->test_requested |= DS_PORT_STATUS_CHANGED;
+ goto exit;
+ }
+
+ ret = dp_link_process_video_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_VIDEO_PATTERN;
+ goto exit;
+ }
+
+ ret = dp_link_process_audio_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_AUDIO_PATTERN;
+ goto exit;
+ }
+
+ pr_debug("done\n");
+exit:
+ return ret;
+}
+
+static u8 *dp_link_get_voltage_swing(struct dp_link *dp_link)
+
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return link->link_status.req_voltage_swing;
+}
+
+static u8 *dp_link_get_pre_emphasis(struct dp_link *dp_link)
+
+{
+ struct dp_link_private *link;
+
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return link->link_status.req_pre_emphasis;
+}
+
+static int dp_link_get_colorimetry_config(struct dp_link *dp_link)
+{
+ u32 cc;
+ enum dynamic_range dr;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ /* unless a video pattern CTS test is ongoing, use CEA_VESA */
+ if (dp_link_is_video_pattern_requested(link))
+ dr = link->request.test_dyn_range;
+ else
+ dr = DP_DYNAMIC_RANGE_RGB_VESA;
+
+ /* Only RGB_VESA nd RGB_CEA supported for now */
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ cc = BIT(3);
+ break;
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ default:
+ cc = 0;
+ }
+
+ return cc;
+}
+
+static bool dp_link_clock_recovery(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return dp_link_clock_recovery_done(link);
+}
+
+static bool dp_link_channel_equalization(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return dp_link_channel_eq_done(link);
+}
+
+static int dp_link_adjust_levels(struct dp_link *dp_link)
+{
+ int i;
+ int max = 0;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ /* use the max level across lanes */
+ for (i = 0; i < dp_link->lane_count; i++) {
+ pr_debug("lane=%d req_voltage_swing=%d\n",
+ i, link->link_status.req_voltage_swing[i]);
+ if (max < link->link_status.req_voltage_swing[i])
+ max = link->link_status.req_voltage_swing[i];
+ }
+
+ dp_link->v_level = max;
+
+ /* use the max level across lanes */
+ max = 0;
+ for (i = 0; i < dp_link->lane_count; i++) {
+ pr_debug("lane=%d req_pre_emphasis=%d\n",
+ i, link->link_status.req_pre_emphasis[i]);
+ if (max < link->link_status.req_pre_emphasis[i])
+ max = link->link_status.req_pre_emphasis[i];
+ }
+
+ dp_link->p_level = max;
+
+ /**
+ * Adjust the voltage swing and pre-emphasis level combination to within
+ * the allowable range.
+ */
+ if (dp_link->v_level > DP_LINK_VOLTAGE_MAX) {
+ pr_debug("Requested vSwingLevel=%d, change to %d\n",
+ dp_link->v_level, DP_LINK_VOLTAGE_MAX);
+ dp_link->v_level = DP_LINK_VOLTAGE_MAX;
+ }
+
+ if (dp_link->p_level > DP_LINK_PRE_EMPHASIS_MAX) {
+ pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+ dp_link->p_level, DP_LINK_PRE_EMPHASIS_MAX);
+ dp_link->p_level = DP_LINK_PRE_EMPHASIS_MAX;
+ }
+
+ if ((dp_link->p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
+ && (dp_link->v_level == DP_LINK_VOLTAGE_LEVEL_2)) {
+ pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+ dp_link->p_level, DP_LINK_PRE_EMPHASIS_LEVEL_1);
+ dp_link->p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
+ }
+
+ pr_debug("v_level=%d, p_level=%d\n",
+ dp_link->v_level, dp_link->p_level);
+
+ return 0;
+}
+
+static int dp_link_send_psm_request(struct dp_link *dp_link, bool req)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return 0;
+}
+
+static u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp)
+{
+ enum test_bit_depth tbd;
+
+ /*
+ * Few simplistic rules and assumptions made here:
+ * 1. Test bit depth is bit depth per color component
+ * 2. Assume 3 color components
+ */
+ switch (bpp) {
+ case 18:
+ tbd = DP_TEST_BIT_DEPTH_6;
+ break;
+ case 24:
+ tbd = DP_TEST_BIT_DEPTH_8;
+ break;
+ case 30:
+ tbd = DP_TEST_BIT_DEPTH_10;
+ break;
+ default:
+ tbd = DP_TEST_BIT_DEPTH_UNKNOWN;
+ break;
+ }
+
+ return tbd;
+}
+
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux)
+{
+ int rc = 0;
+ struct dp_link_private *link;
+ struct dp_link *dp_link;
+
+ if (!dev || !aux) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link->dev = dev;
+ link->aux = aux;
+
+ dp_link = &link->dp_link;
+
+ dp_link->process_request = dp_link_process_request;
+ dp_link->get_voltage_swing = dp_link_get_voltage_swing;
+ dp_link->get_test_bits_depth = dp_link_get_test_bits_depth;
+ dp_link->get_pre_emphasis = dp_link_get_pre_emphasis;
+ dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
+ dp_link->clock_recovery = dp_link_clock_recovery;
+ dp_link->channel_equalization = dp_link_channel_equalization;
+ dp_link->adjust_levels = dp_link_adjust_levels;
+ dp_link->send_psm_request = dp_link_send_psm_request;
+ dp_link->phy_pattern_requested = dp_link_phy_pattern_requested;
+
+ return dp_link;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_link_put(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link)
+ return;
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ devm_kfree(link->dev, link);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
new file mode 100644
index 0000000..de10e9a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -0,0 +1,85 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_LINK_H_
+#define _DP_LINK_H_
+
+#include "dp_aux.h"
+
+enum dp_link_voltage_level {
+ DP_LINK_VOLTAGE_LEVEL_0 = 0,
+ DP_LINK_VOLTAGE_LEVEL_1 = 1,
+ DP_LINK_VOLTAGE_LEVEL_2 = 2,
+ DP_LINK_VOLTAGE_MAX = DP_LINK_VOLTAGE_LEVEL_2,
+};
+
+enum dp_link_preemaphasis_level {
+ DP_LINK_PRE_EMPHASIS_LEVEL_0 = 0,
+ DP_LINK_PRE_EMPHASIS_LEVEL_1 = 1,
+ DP_LINK_PRE_EMPHASIS_LEVEL_2 = 2,
+ DP_LINK_PRE_EMPHASIS_MAX = DP_LINK_PRE_EMPHASIS_LEVEL_2,
+};
+
+enum test_type {
+ UNKNOWN_TEST = 0,
+ TEST_LINK_TRAINING = 0x01,
+ TEST_VIDEO_PATTERN = 0x02,
+ PHY_TEST_PATTERN = 0x08,
+ TEST_EDID_READ = 0x04,
+ TEST_AUDIO_PATTERN = 0x20,
+ TEST_AUDIO_DISABLED_VIDEO = 0x40,
+};
+
+enum status_update {
+ LINK_STATUS_UPDATED = 0x100,
+ DS_PORT_STATUS_CHANGED = 0x200,
+};
+
+struct dp_link {
+ u32 test_requested;
+
+ u32 lane_count;
+ u32 link_rate;
+ u32 v_level;
+ u32 p_level;
+
+ u8 *(*get_voltage_swing)(struct dp_link *dp_link);
+ u8 *(*get_pre_emphasis)(struct dp_link *dp_link);
+ u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
+ int (*process_request)(struct dp_link *dp_link);
+ int (*get_colorimetry_config)(struct dp_link *dp_link);
+ int (*adjust_levels)(struct dp_link *dp_link);
+ int (*send_psm_request)(struct dp_link *dp_link, bool req);
+ bool (*clock_recovery)(struct dp_link *dp_link);
+ bool (*channel_equalization)(struct dp_link *dp_link);
+ bool (*phy_pattern_requested)(struct dp_link *dp_link);
+};
+
+/**
+ * dp_link_get() - get the functionalities of dp test module
+ *
+ *
+ * return: a pointer to dp_link struct
+ */
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux);
+
+/**
+ * dp_link_put() - releases the dp test module's resources
+ *
+ * @dp_link: an instance of dp_link module
+ *
+ */
+void dp_link_put(struct dp_link *dp_link);
+
+#endif /* _DP_LINK_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
new file mode 100644
index 0000000..f9616c4
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -0,0 +1,468 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include "dp_panel.h"
+
+#define DP_LINK_RATE_MULTIPLIER 27000000
+
+struct dp_panel_private {
+ struct device *dev;
+ struct dp_panel dp_panel;
+ struct dp_aux *aux;
+ struct dp_catalog_panel *catalog;
+};
+
+static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
+{
+ u8 *bp;
+ u8 data;
+ u32 const addr = 0x0;
+ u32 const len = 16;
+ int rlen, rc = 0;
+ struct dp_panel_private *panel;
+ struct dp_panel_dpcd *cap;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ cap = &dp_panel->dpcd;
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ rlen = panel->aux->read(panel->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen != len) {
+ pr_err("dpcd read failed, rlen=%d\n", rlen);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ memset(cap, 0, sizeof(*cap));
+
+ data = *bp++; /* byte 0 */
+ cap->major = (data >> 4) & 0x0f;
+ cap->minor = data & 0x0f;
+ pr_debug("version: %d.%d\n", cap->major, cap->minor);
+
+ data = *bp++; /* byte 1 */
+ /* 162, 270, 540, 810 MB, symbol rate, NOT bit rate */
+ cap->max_link_rate = data;
+ pr_debug("link_rate=%d\n", cap->max_link_rate);
+
+ data = *bp++; /* byte 2 */
+ if (data & BIT(7))
+ cap->enhanced_frame++;
+
+ if (data & 0x40) {
+ cap->flags |= DPCD_TPS3;
+ pr_debug("pattern 3 supported\n");
+ } else {
+ pr_debug("pattern 3 not supported\n");
+ }
+
+ data &= 0x0f;
+ cap->max_lane_count = data;
+ pr_debug("lane_count=%d\n", cap->max_lane_count);
+
+ data = *bp++; /* byte 3 */
+ if (data & BIT(0)) {
+ cap->flags |= DPCD_MAX_DOWNSPREAD_0_5;
+ pr_debug("max_downspread\n");
+ }
+
+ if (data & BIT(6)) {
+ cap->flags |= DPCD_NO_AUX_HANDSHAKE;
+ pr_debug("NO Link Training\n");
+ }
+
+ data = *bp++; /* byte 4 */
+ cap->num_rx_port = (data & BIT(0)) + 1;
+ pr_debug("rx_ports=%d", cap->num_rx_port);
+
+ data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
+ cap->downstream_port.dfp_present = data & BIT(0);
+ cap->downstream_port.dfp_type = data & 0x6;
+ cap->downstream_port.format_conversion = data & BIT(3);
+ cap->downstream_port.detailed_cap_info_available = data & BIT(4);
+ pr_debug("dfp_present = %d, dfp_type = %d\n",
+ cap->downstream_port.dfp_present,
+ cap->downstream_port.dfp_type);
+ pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
+ cap->downstream_port.format_conversion,
+ cap->downstream_port.detailed_cap_info_available);
+
+ bp += 1; /* Skip Byte 6 */
+ rlen -= 1;
+
+ data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
+ cap->downstream_port.dfp_count = data & 0x7;
+ 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",
+ cap->downstream_port.dfp_count,
+ 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);
+
+ bp += 2; /* skip 10, 11 port1 capability */
+ rlen -= 2;
+
+ data = *bp++; /* byte 12 */
+ cap->i2c_speed_ctrl = data;
+ if (cap->i2c_speed_ctrl > 0)
+ pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl);
+
+ data = *bp++; /* byte 13 */
+ cap->scrambler_reset = data & BIT(0);
+ pr_debug("scrambler_reset=%d\n", cap->scrambler_reset);
+
+ if (data & BIT(1))
+ cap->enhanced_frame++;
+
+ pr_debug("enhanced_framing=%d\n", cap->enhanced_frame);
+
+ data = *bp++; /* byte 14 */
+ if (data == 0)
+ cap->training_read_interval = 4000; /* us */
+ else
+ cap->training_read_interval = 4000 * data; /* us */
+ pr_debug("training_interval=%d\n", cap->training_read_interval);
+end:
+ return rc;
+}
+
+/*
+ * edid standard header bytes
+ */
+static u8 edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+
+static bool dp_panel_is_edid_header_valid(u8 *buf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_hdr); i++) {
+ if (buf[i] != edid_hdr[i])
+ return false;
+ }
+
+ return true;
+}
+
+static int dp_panel_validate_edid(u8 *bp, int len)
+{
+ int i;
+ u8 csum = 0;
+ u32 const size = 128;
+
+ if (len < size) {
+ pr_err("Error: len=%x\n", len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++)
+ csum += *bp++;
+
+ if (csum != 0) {
+ pr_err("error: csum=0x%x\n", csum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dp_panel_read_edid(struct dp_panel *dp_panel)
+{
+ u8 *edid_buf;
+ u32 checksum = 0;
+ int rlen, ret = 0;
+ int edid_blk = 0, blk_num = 0, retries = 10;
+ u32 const segment_addr = 0x30;
+ bool edid_parsing_done = false;
+ struct dp_panel_private *panel;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ ret = panel->aux->ready(panel->aux);
+ if (!ret) {
+ pr_err("aux chan NOT ready\n");
+ goto end;
+ }
+
+ do {
+ u8 segment;
+
+
+ /*
+ * 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;
+
+ panel->aux->write(panel->aux, segment_addr, 1, AUX_I2C,
+ &segment);
+
+ rlen = panel->aux->read(panel->aux, EDID_START_ADDRESS +
+ (blk_num * EDID_BLOCK_SIZE),
+ EDID_BLOCK_SIZE, AUX_I2C, &edid_buf);
+ if (rlen != EDID_BLOCK_SIZE) {
+ pr_err("invalid edid len: %d\n", rlen);
+ continue;
+ }
+
+ pr_debug("=== EDID data ===\n");
+ print_hex_dump(KERN_DEBUG, "EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ edid_buf, EDID_BLOCK_SIZE, false);
+
+ pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen);
+
+ if (dp_panel_is_edid_header_valid(edid_buf)) {
+ ret = dp_panel_validate_edid(edid_buf, rlen);
+ if (ret) {
+ pr_err("corrupt edid block detected\n");
+ goto end;
+ }
+
+ if (edid_parsing_done) {
+ blk_num++;
+ continue;
+ }
+
+ dp_panel->edid.ext_block_cnt = edid_buf[0x7E];
+ edid_parsing_done = true;
+ checksum = edid_buf[rlen - 1];
+ } else {
+ edid_blk++;
+ blk_num++;
+ }
+
+ memcpy(dp_panel->edid.buf + (edid_blk * EDID_BLOCK_SIZE),
+ edid_buf, EDID_BLOCK_SIZE);
+
+ if (edid_blk == dp_panel->edid.ext_block_cnt)
+ goto end;
+ } while (retries--);
+end:
+ return ret;
+}
+
+static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
+{
+ int rc = 0;
+ u32 data, total_ver, total_hor;
+ struct dp_catalog_panel *catalog;
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ catalog = panel->catalog;
+ pinfo = &panel->dp_panel.pinfo;
+
+ pr_debug("width=%d hporch= %d %d %d\n",
+ pinfo->h_active, pinfo->h_back_porch,
+ pinfo->h_front_porch, pinfo->h_sync_width);
+
+ pr_debug("height=%d vporch= %d %d %d\n",
+ pinfo->v_active, pinfo->v_back_porch,
+ pinfo->v_front_porch, pinfo->v_sync_width);
+
+ total_hor = pinfo->h_active + pinfo->h_back_porch +
+ pinfo->h_front_porch + pinfo->h_sync_width;
+
+ total_ver = pinfo->v_active + pinfo->v_back_porch +
+ pinfo->v_front_porch + pinfo->v_sync_width;
+
+ data = total_ver;
+ data <<= 16;
+ data |= total_hor;
+
+ catalog->total = data;
+
+ data = (pinfo->v_back_porch + pinfo->v_sync_width);
+ data <<= 16;
+ data |= (pinfo->h_back_porch + pinfo->h_sync_width);
+
+ catalog->sync_start = data;
+
+ data = pinfo->v_sync_width;
+ data <<= 16;
+ data |= (pinfo->v_active_low << 31);
+ data |= pinfo->h_sync_width;
+ data |= (pinfo->h_active_low << 15);
+
+ catalog->width_blanking = data;
+
+ data = pinfo->v_active;
+ data <<= 16;
+ data |= pinfo->h_active;
+
+ catalog->dp_active = data;
+
+ panel->catalog->timing_cfg(catalog);
+end:
+ return rc;
+}
+
+static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
+{
+ int rc = 0;
+ struct dp_panel_private *panel;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+end:
+ return rc;
+}
+
+static u8 dp_panel_get_link_rate(struct dp_panel *dp_panel)
+{
+ const u32 encoding_factx10 = 8;
+ const u32 ln_to_link_ratio = 10;
+ u32 min_link_rate, reminder = 0;
+ u8 calc_link_rate = 0, lane_cnt;
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ lane_cnt = dp_panel->dpcd.max_lane_count;
+ pinfo = &dp_panel->pinfo;
+
+ pinfo->bpp = 24;
+
+ /*
+ * The max pixel clock supported is 675Mhz. The
+ * current calculations below will make sure
+ * the min_link_rate is within 32 bit limits.
+ * Any changes in the section of code should
+ * consider this limitation.
+ */
+ min_link_rate = (u32)div_u64(pinfo->pixel_clk_khz * 1000,
+ (lane_cnt * encoding_factx10));
+ min_link_rate /= ln_to_link_ratio;
+ min_link_rate = (min_link_rate * pinfo->bpp);
+ min_link_rate = (u32)div_u64_rem(min_link_rate * 10,
+ DP_LINK_RATE_MULTIPLIER, &reminder);
+
+ /*
+ * To avoid any fractional values,
+ * increment the min_link_rate
+ */
+ if (reminder)
+ min_link_rate += 1;
+ pr_debug("min_link_rate = %d\n", min_link_rate);
+
+ if (min_link_rate <= DP_LINK_RATE_162)
+ calc_link_rate = DP_LINK_RATE_162;
+ else if (min_link_rate <= DP_LINK_RATE_270)
+ calc_link_rate = DP_LINK_RATE_270;
+ else if (min_link_rate <= DP_LINK_RATE_540)
+ calc_link_rate = DP_LINK_RATE_540;
+ else if (min_link_rate <= DP_LINK_RATE_810)
+ calc_link_rate = DP_LINK_RATE_810;
+ else {
+ /* Cap the link rate to the max supported rate */
+ pr_debug("link_rate = %d is unsupported\n", min_link_rate);
+ calc_link_rate = DP_LINK_RATE_810;
+ }
+
+ if (calc_link_rate > dp_panel->dpcd.max_link_rate)
+ calc_link_rate = dp_panel->dpcd.max_link_rate;
+
+ pr_debug("calc_link_rate = 0x%x\n", calc_link_rate);
+end:
+ return calc_link_rate;
+}
+
+struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
+ struct dp_catalog_panel *catalog)
+{
+ int rc = 0;
+ struct dp_panel_private *panel;
+ struct dp_panel *dp_panel;
+
+ if (!dev || !aux || !catalog) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ panel->dev = dev;
+ panel->aux = aux;
+ panel->catalog = catalog;
+
+ dp_panel = &panel->dp_panel;
+
+ dp_panel->edid.buf = devm_kzalloc(dev,
+ sizeof(EDID_BLOCK_SIZE) * 4, GFP_KERNEL);
+
+ dp_panel->init_info = dp_panel_init_panel_info;
+ dp_panel->timing_cfg = dp_panel_timing_cfg;
+ dp_panel->read_edid = dp_panel_read_edid;
+ dp_panel->read_dpcd = dp_panel_read_dpcd;
+ dp_panel->get_link_rate = dp_panel_get_link_rate;
+
+ return dp_panel;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_panel_put(struct dp_panel *dp_panel)
+{
+ struct dp_panel_private *panel;
+
+ if (!dp_panel)
+ return;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ devm_kfree(panel->dev, dp_panel->edid.buf);
+ devm_kfree(panel->dev, panel);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
new file mode 100644
index 0000000..5c145eb
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -0,0 +1,114 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_PANEL_H_
+#define _DP_PANEL_H_
+
+#include "dp_aux.h"
+
+#define DPCD_ENHANCED_FRAME BIT(0)
+#define DPCD_TPS3 BIT(1)
+#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
+#define DPCD_NO_AUX_HANDSHAKE BIT(3)
+#define DPCD_PORT_0_EDID_PRESENTED BIT(4)
+
+#define EDID_START_ADDRESS 0x50
+#define EDID_BLOCK_SIZE 0x80
+
+
+#define DP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */
+#define DP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */
+#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */
+#define DP_LINK_RATE_810 30 /* 8.10G = 270M * 30 */
+#define DP_LINK_RATE_MAX DP_LINK_RATE_810
+
+struct downstream_port_config {
+ /* Byte 02205h */
+ bool dfp_present;
+ u32 dfp_type;
+ bool format_conversion;
+ bool detailed_cap_info_available;
+ /* Byte 02207h */
+ u32 dfp_count;
+ bool msa_timing_par_ignored;
+ bool oui_support;
+};
+
+struct dp_panel_dpcd {
+ u8 major;
+ u8 minor;
+ u8 max_lane_count;
+ u8 num_rx_port;
+ u8 i2c_speed_ctrl;
+ u8 scrambler_reset;
+ u8 enhanced_frame;
+ u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */
+ u32 flags;
+ u32 rx_port0_buf_size;
+ u32 training_read_interval;/* us */
+ struct downstream_port_config downstream_port;
+};
+
+struct dp_panel_edid {
+ u8 *buf;
+ u8 id_name[4];
+ u8 id_product;
+ u8 version;
+ u8 revision;
+ u8 video_intf; /* dp == 0x5 */
+ u8 color_depth; /* 6, 8, 10, 12 and 14 bits */
+ u8 color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+ u8 dpm; /* display power management */
+ u8 sync_digital; /* 1 = digital */
+ u8 sync_separate; /* 1 = separate */
+ u8 vsync_pol; /* 0 = negative, 1 = positive */
+ u8 hsync_pol; /* 0 = negative, 1 = positive */
+ u8 ext_block_cnt;
+};
+
+struct dp_panel_info {
+ u32 h_active;
+ u32 v_active;
+ u32 h_back_porch;
+ u32 h_front_porch;
+ u32 h_sync_width;
+ u32 h_active_low;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 v_sync_width;
+ u32 v_active_low;
+ u32 h_skew;
+ u32 refresh_rate;
+ u32 pixel_clk_khz;
+ u32 bpp;
+};
+
+struct dp_panel {
+ struct dp_panel_dpcd dpcd;
+ struct dp_panel_edid edid;
+ struct dp_panel_info pinfo;
+
+ u32 vic;
+
+ int (*init_info)(struct dp_panel *dp_panel);
+ int (*timing_cfg)(struct dp_panel *dp_panel);
+ int (*read_edid)(struct dp_panel *dp_panel);
+ int (*read_dpcd)(struct dp_panel *dp_panel);
+ u8 (*get_link_rate)(struct dp_panel *dp_panel);
+};
+
+struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
+ struct dp_catalog_panel *catalog);
+void dp_panel_put(struct dp_panel *dp_panel);
+#endif /* _DP_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 5dcdf46..2d7b174 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -142,7 +142,8 @@
void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode,
u32 h_stride,
- u32 vc_id);
+ u32 vc_id,
+ struct dsi_rect *roi);
void dsi_ctrl_hw_cmn_phy_sw_reset(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_cmn_soft_reset(struct dsi_ctrl_hw *ctrl);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index f187ad1..39b797e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1558,7 +1558,6 @@
int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
{
- struct dsi_mode_info video_timing;
int rc = 0;
if (!dsi_ctrl) {
@@ -1568,12 +1567,6 @@
mutex_lock(&dsi_ctrl->ctrl_lock);
- /* replace video mode width with actual roi width */
- memcpy(&video_timing, &dsi_ctrl->host_config.video_timing,
- sizeof(video_timing));
- video_timing.h_active = dsi_ctrl->roi.w;
- video_timing.v_active = dsi_ctrl->roi.h;
-
dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
&dsi_ctrl->host_config.lane_map);
@@ -1586,9 +1579,10 @@
&dsi_ctrl->host_config.u.cmd_engine);
dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
- &video_timing,
- video_timing.h_active * 3,
- 0x0);
+ &dsi_ctrl->host_config.video_timing,
+ dsi_ctrl->host_config.video_timing.h_active * 3,
+ 0x0,
+ &dsi_ctrl->roi);
dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, true);
} else {
dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
@@ -1690,7 +1684,8 @@
dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
&dsi_ctrl->host_config.video_timing,
dsi_ctrl->host_config.video_timing.h_active * 3,
- 0x0);
+ 0x0,
+ NULL);
} else {
dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
&dsi_ctrl->host_config.common_config,
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index 859d707..bb72807 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -319,7 +319,8 @@
void (*setup_cmd_stream)(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode,
u32 h_stride,
- u32 vc_id);
+ u32 vc_id,
+ struct dsi_rect *roi);
/**
* ctrl_en() - enable DSI controller engine
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index 48c2370..a024c43 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -19,6 +19,7 @@
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
+#include "dsi_panel.h"
#define MMSS_MISC_CLAMP_REG_OFF 0x0014
#define DSI_CTRL_DYNAMIC_FORCE_ON (0x23F|BIT(8)|BIT(9)|BIT(11)|BIT(21))
@@ -234,21 +235,36 @@
void dsi_ctrl_hw_cmn_setup_cmd_stream(struct dsi_ctrl_hw *ctrl,
struct dsi_mode_info *mode,
u32 h_stride,
- u32 vc_id)
+ u32 vc_id,
+ struct dsi_rect *roi)
{
- u32 reg = 0;
u32 width_final, stride_final;
+ u32 height_final;
+ u32 stream_total = 0, stream_ctrl = 0;
+ u32 reg_ctrl = 0, reg_ctrl2 = 0;
+
+ if (roi && (!roi->w || !roi->h))
+ return;
if (mode->dsc_enabled && mode->dsc) {
+ u32 reg = 0;
u32 offset = 0;
- u32 reg_ctrl, reg_ctrl2;
+ int pic_width, this_frame_slices, intf_ip_w;
+ struct msm_display_dsc_info dsc;
+
+ memcpy(&dsc, mode->dsc, sizeof(dsc));
+ pic_width = roi ? roi->w : mode->h_active;
+ this_frame_slices = pic_width / dsc.slice_width;
+ intf_ip_w = this_frame_slices * dsc.slice_width;
+ dsi_dsc_pclk_param_calc(&dsc, intf_ip_w);
if (vc_id != 0)
offset = 16;
reg_ctrl = DSI_R32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL);
reg_ctrl2 = DSI_R32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2);
- width_final = mode->dsc->pclk_per_line;
- stride_final = mode->dsc->bytes_per_pkt;
+ width_final = dsc.pclk_per_line;
+ stride_final = dsc.bytes_per_pkt;
+ height_final = roi ? roi->h : mode->v_active;
reg = 0x39 << 8;
/*
@@ -258,34 +274,45 @@
* 2 == 4 pkt
* 3 pkt is not support
*/
- if (mode->dsc->pkt_per_line == 4)
- reg |= (mode->dsc->pkt_per_line - 2) << 6;
+ if (dsc.pkt_per_line == 4)
+ reg |= (dsc.pkt_per_line - 2) << 6;
else
- reg |= (mode->dsc->pkt_per_line - 1) << 6;
- reg |= mode->dsc->eol_byte_num << 4;
+ reg |= (dsc.pkt_per_line - 1) << 6;
+ reg |= dsc.eol_byte_num << 4;
reg |= 1;
reg_ctrl &= ~(0xFFFF << offset);
reg_ctrl |= (reg << offset);
reg_ctrl2 &= ~(0xFFFF << offset);
- reg_ctrl2 |= (mode->dsc->bytes_in_slice << offset);
+ reg_ctrl2 |= (dsc.bytes_in_slice << offset);
DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
+
+ pr_debug("ctrl %d reg_ctrl 0x%x reg_ctrl2 0x%x\n", ctrl->index,
+ reg_ctrl, reg_ctrl2);
+ } else if (roi) {
+ width_final = roi->w;
+ stride_final = roi->w * 3;
+ height_final = roi->h;
} else {
width_final = mode->h_active;
stride_final = h_stride;
+ height_final = mode->v_active;
}
- reg = (stride_final + 1) << 16;
- reg |= (vc_id & 0x3) << 8;
- reg |= 0x39; /* packet data type */
+ stream_ctrl = (stride_final + 1) << 16;
+ stream_ctrl |= (vc_id & 0x3) << 8;
+ stream_ctrl |= 0x39; /* packet data type */
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, reg);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, reg);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, stream_ctrl);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, stream_ctrl);
- reg = (mode->v_active << 16) | width_final;
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, reg);
- DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, reg);
+ stream_total = (height_final << 16) | width_final;
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, stream_total);
+ DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, stream_total);
+
+ pr_debug("ctrl %d stream_ctrl 0x%x stream_total 0x%x\n", ctrl->index,
+ stream_ctrl, stream_total);
}
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index 4c9fbbe..f254af5 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -249,4 +249,6 @@
int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx,
struct dsi_rect *roi);
+void dsi_dsc_pclk_param_calc(struct msm_display_dsc_info *dsc, int intf_width);
+
#endif /* _DSI_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index f596989..2cd9aa1 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -110,6 +110,8 @@
PLANE_PROP_ROT_DST_Y,
PLANE_PROP_ROT_DST_W,
PLANE_PROP_ROT_DST_H,
+ PLANE_PROP_PREFILL_SIZE,
+ PLANE_PROP_PREFILL_TIME,
/* enum/bitmask properties */
PLANE_PROP_ROTATION,
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 3061099..acd7af5 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -341,7 +341,8 @@
if (obj->import_attach && mmu->funcs->map_dma_buf) {
ret = mmu->funcs->map_dma_buf(mmu, msm_obj->sgt,
obj->import_attach->dmabuf,
- DMA_BIDIRECTIONAL);
+ DMA_BIDIRECTIONAL,
+ msm_obj->flags);
if (ret) {
DRM_ERROR("Unable to map dma buf\n");
return ret;
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 2cf170d..19c7726 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -23,6 +23,7 @@
/* Additional internal-use only BO flags: */
#define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */
+#define MSM_BO_KEEPATTRS 0x20000000 /* keep h/w bus attributes */
struct msm_gem_object {
struct drm_gem_object base;
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index ee93339..fbf7e7b 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -43,7 +43,7 @@
void (*unmap_sg)(struct msm_mmu *mmu, struct sg_table *sgt,
enum dma_data_direction dir);
int (*map_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir);
+ struct dma_buf *dma_buf, int dir, u32 flags);
void (*unmap_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt,
struct dma_buf *dma_buf, int dir);
void (*destroy)(struct msm_mmu *mmu);
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index c279d01..4d45898 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -25,6 +25,7 @@
#include <soc/qcom/secure_buffer.h>
#include "msm_drv.h"
+#include "msm_gem.h"
#include "msm_mmu.h"
#ifndef SZ_4G
@@ -220,14 +221,18 @@
}
static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt,
- struct dma_buf *dma_buf, int dir)
+ struct dma_buf *dma_buf, int dir, u32 flags)
{
struct msm_smmu *smmu = to_msm_smmu(mmu);
struct msm_smmu_client *client = msm_smmu_to_client(smmu);
+ unsigned long attrs = 0x0;
int ret;
- ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents, dir,
- dma_buf);
+ if (flags & MSM_BO_KEEPATTRS)
+ attrs |= DMA_ATTR_IOMMU_USE_UPSTREAM_HINT;
+
+ ret = msm_dma_map_sg_attrs(client->dev, sgt->sgl, sgt->nents, dir,
+ dma_buf, attrs);
if (ret != sgt->nents) {
DRM_ERROR("dma map sg failed\n");
return -ENOMEM;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 71e64e4..497d0db 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -282,7 +282,7 @@
* Returns: Pointer to associated private display structure
*/
#define sde_connector_get_panel(C) \
- ((C) ? to_sde_connector((C))->panel : 0)
+ ((C) ? to_sde_connector((C))->panel : NULL)
/**
* sde_connector_get_encoder - get sde connector's private encoder pointer
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 5adef2d..1b40161 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -428,6 +428,103 @@
sde_kms->irq_obj.total_irqs = 0;
}
+static void sde_core_irq_mask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != NULL);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ /* memory barrier */
+ smp_mb__before_atomic();
+ clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ /* memory barrier */
+ smp_mb__after_atomic();
+}
+
+static void sde_core_irq_unmask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != NULL);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ /* memory barrier */
+ smp_mb__before_atomic();
+ set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ /* memory barrier */
+ smp_mb__after_atomic();
+}
+
+static struct irq_chip sde_core_irq_chip = {
+ .name = "sde",
+ .irq_mask = sde_core_irq_mask,
+ .irq_unmask = sde_core_irq_unmask,
+};
+
+static int sde_core_irqdomain_map(struct irq_domain *domain,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ struct sde_kms *sde_kms;
+ int rc;
+
+ if (!domain || !domain->host_data) {
+ SDE_ERROR("invalid parameters domain %d\n", domain != NULL);
+ return -EINVAL;
+ }
+ sde_kms = domain->host_data;
+
+ irq_set_chip_and_handler(irq, &sde_core_irq_chip, handle_level_irq);
+ rc = irq_set_chip_data(irq, sde_kms);
+
+ return rc;
+}
+
+static const struct irq_domain_ops sde_core_irqdomain_ops = {
+ .map = sde_core_irqdomain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+int sde_core_irq_domain_add(struct sde_kms *sde_kms)
+{
+ struct device *dev;
+ struct irq_domain *domain;
+
+ if (!sde_kms->dev || !sde_kms->dev->dev) {
+ pr_err("invalid device handles\n");
+ return -EINVAL;
+ }
+
+ dev = sde_kms->dev->dev;
+
+ domain = irq_domain_add_linear(dev->of_node, 32,
+ &sde_core_irqdomain_ops, sde_kms);
+ if (!domain) {
+ pr_err("failed to add irq_domain\n");
+ return -EINVAL;
+ }
+
+ sde_kms->irq_controller.enabled_mask = 0;
+ sde_kms->irq_controller.domain = domain;
+
+ return 0;
+}
+
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms)
+{
+ if (sde_kms->irq_controller.domain) {
+ irq_domain_remove(sde_kms->irq_controller.domain);
+ sde_kms->irq_controller.domain = NULL;
+ }
+ return 0;
+}
+
irqreturn_t sde_core_irq(struct sde_kms *sde_kms)
{
/*
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.h b/drivers/gpu/drm/msm/sde/sde_core_irq.h
index 64f4160..c775f8c 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.h
@@ -38,6 +38,20 @@
void sde_core_irq_uninstall(struct sde_kms *sde_kms);
/**
+ * sde_core_irq_domain_add - Add core IRQ domain for SDE
+ * @sde_kms: SDE handle
+ * @return: none
+ */
+int sde_core_irq_domain_add(struct sde_kms *sde_kms);
+
+/**
+ * sde_core_irq_domain_fini - uninstall core IRQ domain
+ * @sde_kms: SDE handle
+ * @return: 0 if success; error code otherwise
+ */
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms);
+
+/**
* sde_core_irq - core IRQ handler
* @sde_kms: SDE handle
* @return: interrupt handling status
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 7671649..448a1e7 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -42,6 +42,23 @@
SDE_PERF_MODE_MAX
};
+/**
+ * enum sde_perf_vote_mode: perf vote mode.
+ * @APPS_RSC_MODE: It combines the vote for all displays and votes it
+ * through APPS rsc. This is default mode when display
+ * rsc is not available.
+ * @DISP_RSC_MODE: It combines the vote for all displays and votes it
+ * through display rsc. This is default configuration
+ * when display rsc is available.
+ * @DISP_RSC_PRIMARY_MODE: The primary display votes through display rsc
+ * while all other displays votes through apps rsc.
+ */
+enum sde_perf_vote_mode {
+ APPS_RSC_MODE,
+ DISP_RSC_MODE,
+ DISP_RSC_PRIMARY_MODE,
+};
+
static struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv;
@@ -169,7 +186,9 @@
SDE_DEBUG("final threshold bw limit = %d\n", threshold);
- if (!threshold) {
+ if (!sde_cstate->bw_control) {
+ SDE_DEBUG("bypass bandwidth check\n");
+ } else if (!threshold) {
sde_cstate->new_perf = sde_cstate->cur_perf;
SDE_ERROR("no bandwidth limits specified\n");
return -E2BIG;
@@ -182,12 +201,38 @@
return 0;
}
+static inline bool _is_crtc_client_type_matches(struct drm_crtc *tmp_crtc,
+ enum sde_crtc_client_type curr_client_type,
+ struct sde_core_perf *perf)
+{
+ if (!tmp_crtc)
+ return false;
+ else if (perf->bw_vote_mode == DISP_RSC_PRIMARY_MODE &&
+ perf->sde_rsc_available)
+ return curr_client_type == sde_crtc_get_client_type(tmp_crtc);
+ else
+ return true;
+}
+
+static inline enum sde_crtc_client_type _get_sde_client_type(
+ enum sde_crtc_client_type curr_client_type,
+ struct sde_core_perf *perf)
+{
+ if (perf->bw_vote_mode == DISP_RSC_PRIMARY_MODE &&
+ perf->sde_rsc_available)
+ return curr_client_type;
+ else if (perf->bw_vote_mode != APPS_RSC_MODE && perf->sde_rsc_available)
+ return RT_RSC_CLIENT;
+ else
+ return RT_CLIENT;
+}
+
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
struct drm_crtc *crtc)
{
u64 bw_sum_of_intfs = 0, bus_ab_quota, bus_ib_quota;
struct sde_core_perf_params perf = {0};
- enum sde_crtc_client_type curr_client_type
+ enum sde_crtc_client_type client_vote, curr_client_type
= sde_crtc_get_client_type(crtc);
struct drm_crtc *tmp_crtc;
struct sde_crtc_state *sde_cstate;
@@ -195,7 +240,8 @@
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
- (curr_client_type == sde_crtc_get_client_type(tmp_crtc))) {
+ _is_crtc_client_type_matches(tmp_crtc, curr_client_type,
+ &kms->perf)) {
sde_cstate = to_sde_crtc_state(tmp_crtc->state);
perf.max_per_pipe_ib = max(perf.max_per_pipe_ib,
@@ -217,7 +263,8 @@
bus_ib_quota = kms->perf.fix_core_ib_vote;
}
- switch (curr_client_type) {
+ client_vote = _get_sde_client_type(curr_client_type, &kms->perf);
+ switch (client_vote) {
case NRT_CLIENT:
sde_power_data_bus_set_quota(&priv->phandle, kms->core_client,
SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT,
@@ -246,6 +293,32 @@
SDE_ERROR("invalid client type:%d\n", curr_client_type);
break;
}
+
+ if (kms->perf.bw_vote_mode_updated) {
+ switch (kms->perf.bw_vote_mode) {
+ case DISP_RSC_MODE:
+ sde_power_data_bus_set_quota(&priv->phandle,
+ kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT, 0, 0);
+ sde_power_data_bus_set_quota(&priv->phandle,
+ kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, 0, 0);
+ kms->perf.bw_vote_mode_updated = false;
+ break;
+
+ case APPS_RSC_MODE:
+ sde_cstate = to_sde_crtc_state(crtc->state);
+ if (sde_cstate->rsc_client) {
+ sde_rsc_client_vote(sde_cstate->rsc_client,
+ 0, 0);
+ kms->perf.bw_vote_mode_updated = false;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
}
/**
@@ -349,6 +422,10 @@
}
priv = kms->dev->dev_private;
+ /* wake vote update is not required with display rsc */
+ if (kms->perf.bw_vote_mode == DISP_RSC_MODE && stop_req)
+ return;
+
sde_crtc = to_sde_crtc(crtc);
sde_cstate = to_sde_crtc_state(crtc->state);
@@ -376,6 +453,21 @@
update_bus = 1;
}
+ /* display rsc override during solver mode */
+ if (kms->perf.bw_vote_mode == DISP_RSC_MODE &&
+ get_sde_rsc_current_state(SDE_RSC_INDEX) ==
+ SDE_RSC_CMD_STATE) {
+ /* update new bandwdith in all cases */
+ if (params_changed && new->bw_ctl != old->bw_ctl) {
+ old->bw_ctl = new->bw_ctl;
+ old->max_per_pipe_ib = new->max_per_pipe_ib;
+ update_bus = 1;
+ /* reduce bw vote is not required in solver mode */
+ } else if (!params_changed) {
+ update_bus = 0;
+ }
+ }
+
if ((params_changed &&
(new->core_clk_rate > old->core_clk_rate)) ||
(!params_changed &&
@@ -535,6 +627,10 @@
(u32 *)&catalog->perf.max_bw_high);
debugfs_create_file("perf_mode", 0644, perf->debugfs_root,
(u32 *)perf, &sde_core_perf_mode_fops);
+ debugfs_create_u32("bw_vote_mode", 0600, perf->debugfs_root,
+ &perf->bw_vote_mode);
+ debugfs_create_bool("bw_vote_mode_updated", 0600, perf->debugfs_root,
+ &perf->bw_vote_mode_updated);
debugfs_create_u64("fix_core_clk_rate", 0644, perf->debugfs_root,
&perf->fix_core_clk_rate);
debugfs_create_u64("fix_core_ib_vote", 0644, perf->debugfs_root,
@@ -566,7 +662,6 @@
sde_core_perf_debugfs_destroy(perf);
perf->max_core_clk_rate = 0;
perf->core_clk = NULL;
- mutex_destroy(&perf->perf_lock);
perf->clk_name = NULL;
perf->phandle = NULL;
perf->catalog = NULL;
@@ -590,7 +685,12 @@
perf->phandle = phandle;
perf->pclient = pclient;
perf->clk_name = clk_name;
- mutex_init(&perf->perf_lock);
+ perf->sde_rsc_available = is_sde_rsc_available(SDE_RSC_INDEX);
+ /* set default mode */
+ if (perf->sde_rsc_available)
+ perf->bw_vote_mode = DISP_RSC_MODE;
+ else
+ perf->bw_vote_mode = APPS_RSC_MODE;
perf->core_clk = sde_power_clk_get_clk(phandle, clk_name);
if (!perf->core_clk) {
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index 31851be..4a1bdad 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -22,8 +22,6 @@
#include "sde_power_handle.h"
#define SDE_PERF_DEFAULT_MAX_CORE_CLK_RATE 320000000
-#define SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA 2000000000
-#define SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA 2000000000
/**
* struct sde_core_perf_params - definition of performance parameters
@@ -53,7 +51,6 @@
* struct sde_core_perf - definition of core performance context
* @dev: Pointer to drm device
* @debugfs_root: top level debug folder
- * @perf_lock: serialization lock for this context
* @catalog: Pointer to catalog configuration
* @phandle: Pointer to power handler
* @pclient: Pointer to power client
@@ -66,11 +63,13 @@
* @fix_core_clk_rate: fixed core clock request in Hz used in mode 2
* @fix_core_ib_vote: fixed core ib vote in bps used in mode 2
* @fix_core_ab_vote: fixed core ab vote in bps used in mode 2
+ * @bw_vote_mode: apps rsc vs display rsc bandwidth vote mode
+ * @sde_rsc_available: is display rsc available
+ * @bw_vote_mode_updated: bandwidth vote mode update
*/
struct sde_core_perf {
struct drm_device *dev;
struct dentry *debugfs_root;
- struct mutex perf_lock;
struct sde_mdss_cfg *catalog;
struct sde_power_handle *phandle;
struct sde_power_client *pclient;
@@ -83,6 +82,9 @@
u64 fix_core_clk_rate;
u64 fix_core_ib_vote;
u64 fix_core_ab_vote;
+ u32 bw_vote_mode;
+ bool sde_rsc_available;
+ bool bw_vote_mode_updated;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index a457938..7d0fad0 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -612,12 +612,15 @@
int i;
if (!dim_layer->rect.w || !dim_layer->rect.h) {
- SDE_DEBUG("empty dim layer\n");
+ SDE_DEBUG("empty dim_layer\n");
return;
}
cstate = to_sde_crtc_state(crtc->state);
+ SDE_DEBUG("dim_layer - flags:%d, stage:%d\n",
+ dim_layer->flags, dim_layer->stage);
+
split_dim_layer.stage = dim_layer->stage;
split_dim_layer.color_fill = dim_layer->color_fill;
@@ -651,9 +654,13 @@
} else {
split_dim_layer.rect.x =
split_dim_layer.rect.x -
- cstate->lm_bounds[i].w;
+ cstate->lm_bounds[i].x;
}
+ SDE_DEBUG("split_dim_layer - LM:%d, rect:{%d,%d,%d,%d}}\n",
+ i, split_dim_layer.rect.x, split_dim_layer.rect.y,
+ split_dim_layer.rect.w, split_dim_layer.rect.h);
+
lm = mixer[i].hw_lm;
mixer[i].mixer_op_mode |= 1 << split_dim_layer.stage;
lm->ops.setup_dim_layer(lm, &split_dim_layer);
@@ -858,9 +865,24 @@
sde_crtc = to_sde_crtc(crtc);
crtc_state = to_sde_crtc_state(state);
- for (i = 0; i < sde_crtc->num_mixers; i++) {
- if (!sde_kms_rect_is_null(&crtc_state->lm_roi[i]))
- disp_bitmask |= BIT(i);
+ /* pingpong split: one ROI, one LM, two physical displays */
+ if (crtc_state->is_ppsplit) {
+ u32 lm_split_width = crtc_state->lm_bounds[0].w / 2;
+ struct sde_rect *roi = &crtc_state->lm_roi[0];
+
+ if (sde_kms_rect_is_null(roi))
+ disp_bitmask = 0;
+ else if ((u32)roi->x + (u32)roi->w <= lm_split_width)
+ disp_bitmask = BIT(0); /* left only */
+ else if (roi->x >= lm_split_width)
+ disp_bitmask = BIT(1); /* right only */
+ else
+ disp_bitmask = BIT(0) | BIT(1); /* left and right */
+ } else {
+ for (i = 0; i < sde_crtc->num_mixers; i++) {
+ if (!sde_kms_rect_is_null(&crtc_state->lm_roi[i]))
+ disp_bitmask |= BIT(i);
+ }
}
SDE_DEBUG("affected displays 0x%x\n", disp_bitmask);
@@ -881,9 +903,6 @@
sde_crtc = to_sde_crtc(crtc);
crtc_state = to_sde_crtc_state(state);
- if (sde_crtc->num_mixers == 1)
- return 0;
-
if (sde_crtc->num_mixers > CRTC_DUAL_MIXERS) {
SDE_ERROR("%s: unsupported number of mixers: %d\n",
sde_crtc->name, sde_crtc->num_mixers);
@@ -891,9 +910,41 @@
}
/*
- * On certain HW, ROIs must be centered on the split between LMs,
- * and be of equal width.
+ * If using pingpong split: one ROI, one LM, two physical displays
+ * then the ROI must be centered on the panel split boundary and
+ * be of equal width across the split.
*/
+ if (crtc_state->is_ppsplit) {
+ u16 panel_split_width;
+ u32 display_mask;
+
+ roi[0] = &crtc_state->lm_roi[0];
+
+ if (sde_kms_rect_is_null(roi[0]))
+ return 0;
+
+ display_mask = _sde_crtc_get_displays_affected(crtc, state);
+ if (display_mask != (BIT(0) | BIT(1)))
+ return 0;
+
+ panel_split_width = crtc_state->lm_bounds[0].w / 2;
+ if (roi[0]->x + roi[0]->w / 2 != panel_split_width) {
+ SDE_ERROR("%s: roi x %d w %d split %d\n",
+ sde_crtc->name, roi[0]->x, roi[0]->w,
+ panel_split_width);
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ /*
+ * On certain HW, if using 2 LM, ROIs must be split evenly between the
+ * LMs and be of equal width.
+ */
+ if (sde_crtc->num_mixers == 1)
+ return 0;
+
roi[0] = &crtc_state->lm_roi[0];
roi[1] = &crtc_state->lm_roi[1];
@@ -1181,6 +1232,69 @@
_sde_crtc_program_lm_output_roi(crtc);
}
+static void _sde_crtc_swap_mixers_for_right_partial_update(
+ struct drm_crtc *crtc)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *cstate;
+ struct drm_encoder *drm_enc;
+ bool is_right_only;
+ bool encoder_in_dsc_merge = false;
+
+ if (!crtc || !crtc->state)
+ return;
+
+ sde_crtc = to_sde_crtc(crtc);
+ cstate = to_sde_crtc_state(crtc->state);
+
+ if (sde_crtc->num_mixers != CRTC_DUAL_MIXERS)
+ return;
+
+ drm_for_each_encoder(drm_enc, crtc->dev) {
+ if (drm_enc->crtc == crtc &&
+ sde_encoder_is_dsc_merge(drm_enc)) {
+ encoder_in_dsc_merge = true;
+ break;
+ }
+ }
+
+ /**
+ * For right-only partial update with DSC merge, we swap LM0 & LM1.
+ * This is due to two reasons:
+ * - On 8996, there is a DSC HW requirement that in DSC Merge Mode,
+ * the left DSC must be used, right DSC cannot be used alone.
+ * For right-only partial update, this means swap layer mixers to map
+ * Left LM to Right INTF. On later HW this was relaxed.
+ * - In DSC Merge mode, the physical encoder has already registered
+ * PP0 as the master, to switch to right-only we would have to
+ * reprogram to be driven by PP1 instead.
+ * To support both cases, we prefer to support the mixer swap solution.
+ */
+ if (!encoder_in_dsc_merge)
+ return;
+
+ is_right_only = sde_kms_rect_is_null(&cstate->lm_roi[0]) &&
+ !sde_kms_rect_is_null(&cstate->lm_roi[1]);
+
+ if (is_right_only && !sde_crtc->mixers_swapped) {
+ /* right-only update swap mixers */
+ swap(sde_crtc->mixers[0], sde_crtc->mixers[1]);
+ sde_crtc->mixers_swapped = true;
+ } else if (!is_right_only && sde_crtc->mixers_swapped) {
+ /* left-only or full update, swap back */
+ swap(sde_crtc->mixers[0], sde_crtc->mixers[1]);
+ sde_crtc->mixers_swapped = false;
+ }
+
+ SDE_DEBUG("%s: right_only %d swapped %d, mix0->lm%d, mix1->lm%d\n",
+ sde_crtc->name, is_right_only, sde_crtc->mixers_swapped,
+ sde_crtc->mixers[0].hw_lm->idx - LM_0,
+ sde_crtc->mixers[1].hw_lm->idx - LM_0);
+ SDE_EVT32(DRMID(crtc), is_right_only, sde_crtc->mixers_swapped,
+ sde_crtc->mixers[0].hw_lm->idx - LM_0,
+ sde_crtc->mixers[1].hw_lm->idx - LM_0);
+}
+
/**
* _sde_crtc_blend_setup - configure crtc mixers
* @crtc: Pointer to drm crtc structure
@@ -1226,6 +1340,8 @@
lm->ops.clear_dim_layer(lm);
}
+ _sde_crtc_swap_mixers_for_right_partial_update(crtc);
+
/* initialize stage cfg */
memset(&sde_crtc->stage_cfg, 0, sizeof(struct sde_hw_stage_cfg));
@@ -1534,26 +1650,28 @@
{
struct sde_drm_dim_layer_v1 dim_layer_v1;
struct sde_drm_dim_layer_cfg *user_cfg;
+ struct sde_hw_dim_layer *dim_layer;
u32 count, i;
if (!cstate) {
SDE_ERROR("invalid cstate\n");
return;
}
+ dim_layer = cstate->dim_layer;
if (!usr_ptr) {
- SDE_DEBUG("dim layer data removed\n");
+ SDE_DEBUG("dim_layer data removed\n");
return;
}
if (copy_from_user(&dim_layer_v1, usr_ptr, sizeof(dim_layer_v1))) {
- SDE_ERROR("failed to copy dim layer data\n");
+ SDE_ERROR("failed to copy dim_layer data\n");
return;
}
count = dim_layer_v1.num_layers;
- if (!count || (count > SDE_MAX_DIM_LAYERS)) {
- SDE_ERROR("invalid number of Dim Layers:%d", count);
+ if (count > SDE_MAX_DIM_LAYERS) {
+ SDE_ERROR("invalid number of dim_layers:%d", count);
return;
}
@@ -1561,22 +1679,31 @@
cstate->num_dim_layers = count;
for (i = 0; i < count; i++) {
user_cfg = &dim_layer_v1.layer_cfg[i];
- cstate->dim_layer[i].flags = user_cfg->flags;
- cstate->dim_layer[i].stage = user_cfg->stage + SDE_STAGE_0;
- cstate->dim_layer[i].rect.x = user_cfg->rect.x1;
- cstate->dim_layer[i].rect.y = user_cfg->rect.y1;
- cstate->dim_layer[i].rect.w = user_cfg->rect.x2 -
- user_cfg->rect.x1 + 1;
- cstate->dim_layer[i].rect.h = user_cfg->rect.y2 -
- user_cfg->rect.y1 + 1;
+ dim_layer[i].flags = user_cfg->flags;
+ dim_layer[i].stage = user_cfg->stage + SDE_STAGE_0;
- cstate->dim_layer[i].color_fill = (struct sde_mdss_color) {
+ dim_layer[i].rect.x = user_cfg->rect.x1;
+ dim_layer[i].rect.y = user_cfg->rect.y1;
+ dim_layer[i].rect.w = user_cfg->rect.x2 - user_cfg->rect.x1;
+ dim_layer[i].rect.h = user_cfg->rect.y2 - user_cfg->rect.y1;
+
+ dim_layer[i].color_fill = (struct sde_mdss_color) {
user_cfg->color_fill.color_0,
user_cfg->color_fill.color_1,
user_cfg->color_fill.color_2,
user_cfg->color_fill.color_3,
};
+
+ SDE_DEBUG("dim_layer[%d] - flags:%d, stage:%d\n",
+ i, dim_layer[i].flags, dim_layer[i].stage);
+ SDE_DEBUG(" rect:{%d,%d,%d,%d}, color:{%d,%d,%d,%d}\n",
+ dim_layer[i].rect.x, dim_layer[i].rect.y,
+ dim_layer[i].rect.w, dim_layer[i].rect.h,
+ dim_layer[i].color_fill.color_0,
+ dim_layer[i].color_fill.color_1,
+ dim_layer[i].color_fill.color_2,
+ dim_layer[i].color_fill.color_3);
}
}
@@ -1700,6 +1827,23 @@
mutex_unlock(&sde_crtc->crtc_lock);
}
+static void _sde_crtc_setup_is_ppsplit(struct drm_crtc_state *state)
+{
+ int i;
+ struct sde_crtc_state *cstate;
+
+ cstate = to_sde_crtc_state(state);
+
+ cstate->is_ppsplit = false;
+ for (i = 0; i < cstate->num_connectors; i++) {
+ struct drm_connector *conn = cstate->connectors[i];
+
+ if (sde_connector_get_topology_name(conn) ==
+ SDE_RM_TOPOLOGY_PPSPLIT)
+ cstate->is_ppsplit = true;
+ }
+}
+
static void _sde_crtc_setup_lm_bounds(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
@@ -1764,6 +1908,7 @@
if (!sde_crtc->num_mixers) {
_sde_crtc_setup_mixers(crtc);
+ _sde_crtc_setup_is_ppsplit(crtc->state);
_sde_crtc_setup_lm_bounds(crtc, crtc->state);
}
@@ -1991,7 +2136,13 @@
dev = crtc->dev;
if (enable) {
- if (_sde_crtc_power_enable(sde_crtc, true))
+ int ret;
+
+ /* drop lock since power crtc cb may try to re-acquire lock */
+ mutex_unlock(&sde_crtc->crtc_lock);
+ ret = _sde_crtc_power_enable(sde_crtc, true);
+ mutex_lock(&sde_crtc->crtc_lock);
+ if (ret)
return;
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
@@ -2012,7 +2163,11 @@
sde_encoder_register_vblank_callback(enc, NULL, NULL);
}
+
+ /* drop lock since power crtc cb may try to re-acquire lock */
+ mutex_unlock(&sde_crtc->crtc_lock);
_sde_crtc_power_enable(sde_crtc, false);
+ mutex_lock(&sde_crtc->crtc_lock);
}
}
@@ -2486,6 +2641,7 @@
mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
+ _sde_crtc_setup_is_ppsplit(state);
_sde_crtc_setup_lm_bounds(crtc, state);
/* get plane state for all drm planes associated with crtc state */
@@ -2508,9 +2664,10 @@
/* check dim layer stage with every plane */
for (i = 0; i < cstate->num_dim_layers; i++) {
if (pstates[cnt].stage == cstate->dim_layer[i].stage) {
- SDE_ERROR("plane%d/dimlayer in same stage:%d\n",
- plane->base.id,
- cstate->dim_layer[i].stage);
+ SDE_ERROR(
+ "plane:%d/dim_layer:%i-same stage:%d\n",
+ plane->base.id, i,
+ cstate->dim_layer[i].stage);
rc = -EINVAL;
goto end;
}
@@ -2755,19 +2912,19 @@
CRTC_PROP_CORE_CLK);
msm_property_install_range(&sde_crtc->property_info,
"core_ab", 0x0, 0, U64_MAX,
- SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA,
+ catalog->perf.max_bw_high * 1000ULL,
CRTC_PROP_CORE_AB);
msm_property_install_range(&sde_crtc->property_info,
"core_ib", 0x0, 0, U64_MAX,
- SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA,
+ catalog->perf.max_bw_high * 1000ULL,
CRTC_PROP_CORE_IB);
msm_property_install_range(&sde_crtc->property_info,
"mem_ab", 0x0, 0, U64_MAX,
- SDE_PERF_DEFAULT_MAX_BUS_AB_QUOTA,
+ catalog->perf.max_bw_high * 1000ULL,
CRTC_PROP_MEM_AB);
msm_property_install_range(&sde_crtc->property_info,
"mem_ib", 0x0, 0, U64_MAX,
- SDE_PERF_DEFAULT_MAX_BUS_IB_QUOTA,
+ catalog->perf.max_bw_high * 1000ULL,
CRTC_PROP_MEM_IB);
msm_property_install_range(&sde_crtc->property_info,
"rot_prefill_bw", 0, 0, U64_MAX,
@@ -2781,16 +2938,18 @@
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
- if (catalog->has_dim_layer) {
- msm_property_install_volatile_range(&sde_crtc->property_info,
- "dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1);
- }
-
msm_property_install_volatile_range(&sde_crtc->property_info,
"sde_drm_roi_v1", 0x0, 0, ~0, 0, CRTC_PROP_ROI_V1);
sde_kms_info_reset(info);
+ if (catalog->has_dim_layer) {
+ msm_property_install_volatile_range(&sde_crtc->property_info,
+ "dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1);
+ sde_kms_info_add_keyint(info, "dim_layer_v1_max_layers",
+ SDE_MAX_DIM_LAYERS);
+ }
+
sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion);
sde_kms_info_add_keyint(info, "max_linewidth",
catalog->max_mixer_width);
@@ -2891,6 +3050,12 @@
case CRTC_PROP_ROI_V1:
ret = _sde_crtc_set_roi_v1(state, (void *)val);
break;
+ case CRTC_PROP_CORE_AB:
+ case CRTC_PROP_CORE_IB:
+ case CRTC_PROP_MEM_AB:
+ case CRTC_PROP_MEM_IB:
+ cstate->bw_control = true;
+ break;
default:
/* nothing to do */
break;
@@ -2990,6 +3155,7 @@
struct drm_display_mode *mode;
struct drm_framebuffer *fb;
struct drm_plane_state *state;
+ struct sde_crtc_state *cstate;
int i, out_width;
@@ -2998,6 +3164,7 @@
sde_crtc = s->private;
crtc = &sde_crtc->base;
+ cstate = to_sde_crtc_state(crtc->state);
mutex_lock(&sde_crtc->crtc_lock);
mode = &crtc->state->adjusted_mode;
@@ -3022,6 +3189,23 @@
seq_puts(s, "\n");
+ for (i = 0; i < cstate->num_dim_layers; i++) {
+ struct sde_hw_dim_layer *dim_layer = &cstate->dim_layer[i];
+
+ seq_printf(s, "\tdim_layer:%d] stage:%d flags:%d\n",
+ i, dim_layer->stage, dim_layer->flags);
+ seq_printf(s, "\tdst_x:%d dst_y:%d dst_w:%d dst_h:%d\n",
+ dim_layer->rect.x, dim_layer->rect.y,
+ dim_layer->rect.w, dim_layer->rect.h);
+ seq_printf(s,
+ "\tcolor_0:%d color_1:%d color_2:%d color_3:%d\n",
+ dim_layer->color_fill.color_0,
+ dim_layer->color_fill.color_1,
+ dim_layer->color_fill.color_2,
+ dim_layer->color_fill.color_3);
+ seq_puts(s, "\n");
+ }
+
drm_atomic_crtc_for_each_plane(plane, crtc) {
pstate = to_sde_plane_state(plane->state);
state = plane->state;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 6a22115..4b3c814 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -108,6 +108,8 @@
* @name : ASCII description of this crtc
* @num_ctls : Number of ctl paths in use
* @num_mixers : Number of mixers in use
+ * @mixers_swapped: Whether the mixers have been swapped for left/right update
+ * especially in the case of DSC Merge.
* @mixers : List of active mixers
* @event : Pointer to last received drm vblank event. If there is a
* pending vblank event, this will be non-null.
@@ -147,6 +149,7 @@
/* HW Resources reserved for the crtc */
u32 num_ctls;
u32 num_mixers;
+ bool mixers_swapped;
struct sde_crtc_mixer mixers[CRTC_DUAL_MIXERS];
struct drm_pending_vblank_event *event;
@@ -251,6 +254,8 @@
* @num_connectors: Number of associated drm connectors
* @intf_mode : Interface mode of the primary connector
* @rsc_client : sde rsc client when mode is valid
+ * @is_ppsplit : Whether current topology requires PPSplit special handling
+ * @bw_control : true if bw controlled by bw properties
* @crtc_roi : Current CRTC ROI. Possibly sub-rectangle of mode.
* Origin top left of CRTC.
* @lm_bounds : LM boundaries based on current mode full resolution, no ROI.
@@ -276,7 +281,9 @@
enum sde_intf_mode intf_mode;
struct sde_rsc_client *rsc_client;
bool rsc_update;
+ bool bw_control;
+ bool is_ppsplit;
struct sde_rect crtc_roi;
struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
struct sde_rect lm_roi[CRTC_DUAL_MIXERS];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index f11ba51..e3ad960 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -123,8 +123,11 @@
* @cur_master: Pointer to the current master in this mode. Optimization
* Only valid after enable. Cleared as disable.
* @hw_pp Handle to the pingpong blocks used for the display. No.
- * pingpong blocks can be different than num_phys_encs.
+ * pingpong blocks can be different than num_phys_encs.
* @hw_dsc: Array of DSC block handles used for the display.
+ * @intfs_swapped Whether or not the phys_enc interfaces have been swapped
+ * for partial update right-only cases, such as pingpong
+ * split where virtual pingpong does not generate IRQs
* @crtc_vblank_cb: Callback into the upper layer / CRTC for
* notification of the VBLANK
* @crtc_vblank_cb_data: Data from upper layer for VBLANK notification
@@ -155,6 +158,8 @@
* @topology: topology of the display
* @mode_set_complete: flag to indicate modeset completion
* @rsc_cfg: rsc configuration
+ * @cur_conn_roi: current connector roi
+ * @prv_conn_roi: previous connector roi to optimize if unchanged
*/
struct sde_encoder_virt {
struct drm_encoder base;
@@ -169,6 +174,8 @@
struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+ bool intfs_swapped;
+
void (*crtc_vblank_cb)(void *);
void *crtc_vblank_cb_data;
@@ -195,17 +202,51 @@
bool mode_set_complete;
struct sde_encoder_rsc_config rsc_cfg;
+ struct sde_rect cur_conn_roi;
+ struct sde_rect prv_conn_roi;
};
#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
-inline bool _sde_is_dsc_enabled(struct sde_encoder_virt *sde_enc)
+bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
+
{
- struct msm_compression_info *comp_info = &sde_enc->disp_info.comp_info;
+ struct sde_encoder_virt *sde_enc;
+ struct msm_compression_info *comp_info;
+
+ if (!drm_enc)
+ return false;
+
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ comp_info = &sde_enc->disp_info.comp_info;
return (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC);
}
+bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc)
+{
+ enum sde_rm_topology_name topology;
+ struct sde_encoder_virt *sde_enc;
+ struct drm_connector *drm_conn;
+
+ if (!drm_enc)
+ return false;
+
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ if (!sde_enc->cur_master)
+ return false;
+
+ drm_conn = sde_enc->cur_master->connector;
+ if (!drm_conn)
+ return false;
+
+ topology = sde_connector_get_topology_name(drm_conn);
+ if (topology == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE)
+ return true;
+
+ return false;
+}
+
static inline int _sde_encoder_power_enable(struct sde_encoder_virt *sde_enc,
bool enable)
{
@@ -320,7 +361,22 @@
sde_enc = to_sde_encoder_virt(phys_enc->parent);
hw_mdptop = phys_enc->hw_mdptop;
- cfg.en = phys_enc->split_role != ENC_ROLE_SOLO;
+
+ /**
+ * disable split modes since encoder will be operating in as the only
+ * encoder, either for the entire use case in the case of, for example,
+ * single DSI, or for this frame in the case of left/right only partial
+ * update.
+ */
+ if (phys_enc->split_role == ENC_ROLE_SOLO) {
+ if (hw_mdptop->ops.setup_split_pipe)
+ hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
+ if (hw_mdptop->ops.setup_pp_split)
+ hw_mdptop->ops.setup_pp_split(hw_mdptop, &cfg);
+ return;
+ }
+
+ cfg.en = true;
cfg.mode = phys_enc->intf_mode;
cfg.intf = interface;
@@ -334,8 +390,7 @@
else
cfg.pp_split_slave = INTF_MAX;
- if (phys_enc->split_role != ENC_ROLE_SLAVE) {
- /* master/solo encoder */
+ if (phys_enc->split_role == ENC_ROLE_MASTER) {
SDE_DEBUG_ENC(sde_enc, "enable %d\n", cfg.en);
if (hw_mdptop->ops.setup_split_pipe)
@@ -555,8 +610,14 @@
static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc,
struct sde_hw_pingpong *hw_pp, struct msm_display_dsc_info *dsc,
- u32 common_mode, bool ich_reset)
+ u32 common_mode, bool ich_reset, bool enable)
{
+ if (!enable) {
+ if (hw_pp->ops.disable_dsc)
+ hw_pp->ops.disable_dsc(hw_pp);
+ return;
+ }
+
if (hw_dsc->ops.dsc_config)
hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, ich_reset);
@@ -570,9 +631,27 @@
hw_pp->ops.enable_dsc(hw_pp);
}
+static void _sde_encoder_get_connector_roi(
+ struct sde_encoder_virt *sde_enc,
+ struct sde_rect *merged_conn_roi)
+{
+ struct drm_connector *drm_conn;
+ struct sde_connector_state *c_state;
+
+ if (!sde_enc || !merged_conn_roi)
+ return;
+
+ drm_conn = sde_enc->phys_encs[0]->connector;
+
+ if (!drm_conn || !drm_conn->state)
+ return;
+
+ c_state = to_sde_connector_state(drm_conn->state);
+ sde_kms_rect_merge_rectangles(&c_state->rois, merged_conn_roi);
+}
+
static int _sde_encoder_dsc_1_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc)
{
- int pic_width, pic_height;
int this_frame_slices;
int intf_ip_w, enc_ip_w;
int ich_res, dsc_common_mode = 0;
@@ -580,22 +659,18 @@
struct sde_hw_pingpong *hw_pp = sde_enc->hw_pp[0];
struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0];
struct sde_encoder_phys *enc_master = sde_enc->cur_master;
- struct sde_hw_mdp *hw_mdp_top = enc_master->hw_mdptop;
+ const struct sde_rect *roi = &sde_enc->cur_conn_roi;
struct msm_display_dsc_info *dsc =
&sde_enc->disp_info.comp_info.dsc_info;
- if (dsc == NULL || hw_dsc == NULL || hw_pp == NULL ||
- hw_mdp_top == NULL) {
+ if (dsc == NULL || hw_dsc == NULL || hw_pp == NULL || !enc_master) {
SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
return -EINVAL;
}
- pic_width = dsc->pic_width;
- pic_height = dsc->pic_height;
+ _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h);
- _sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);
-
- this_frame_slices = pic_width / dsc->slice_width;
+ this_frame_slices = roi->w / dsc->slice_width;
intf_ip_w = this_frame_slices * dsc->slice_width;
_sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);
@@ -608,132 +683,208 @@
dsc_common_mode = DSC_MODE_VIDEO;
SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
- pic_width, pic_height, dsc_common_mode);
- SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
- dsc_common_mode);
+ roi->w, roi->h, dsc_common_mode);
+ SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, dsc_common_mode);
_sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode,
- ich_res);
+ ich_res, true);
return 0;
}
-static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc)
+static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc,
+ struct sde_encoder_kickoff_params *params)
{
- int pic_width, pic_height;
int this_frame_slices;
int intf_ip_w, enc_ip_w;
int ich_res, dsc_common_mode;
struct sde_encoder_phys *enc_master = sde_enc->cur_master;
- struct sde_hw_dsc *l_hw_dsc = sde_enc->hw_dsc[0];
- struct sde_hw_dsc *r_hw_dsc = sde_enc->hw_dsc[1];
- struct sde_hw_pingpong *l_hw_pp = sde_enc->hw_pp[0];
- struct sde_hw_pingpong *r_hw_pp = sde_enc->hw_pp[1];
- struct sde_hw_mdp *hw_mdp_top = enc_master->hw_mdptop;
- struct msm_display_dsc_info *dsc =
- &sde_enc->disp_info.comp_info.dsc_info;
+ const struct sde_rect *roi = &sde_enc->cur_conn_roi;
+ struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+ struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+ struct msm_display_dsc_info dsc[MAX_CHANNELS_PER_ENC];
+ bool half_panel_partial_update;
+ int i;
- if (l_hw_dsc == NULL || r_hw_dsc == NULL || hw_mdp_top == NULL ||
- l_hw_pp == NULL || r_hw_pp == NULL) {
- SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
- return -EINVAL;
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ hw_pp[i] = sde_enc->hw_pp[i];
+ hw_dsc[i] = sde_enc->hw_dsc[i];
+
+ if (!hw_pp[i] || !hw_dsc[i]) {
+ SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
+ return -EINVAL;
+ }
}
- pic_width = dsc->pic_width * sde_enc->display_num_of_h_tiles;
- pic_height = dsc->pic_height;
+ half_panel_partial_update =
+ hweight_long(params->affected_displays) == 1;
- _sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);
-
- this_frame_slices = pic_width / dsc->slice_width;
- intf_ip_w = this_frame_slices * dsc->slice_width;
-
- intf_ip_w /= 2;
- _sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);
-
- enc_ip_w = intf_ip_w;
- _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
-
- ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc);
-
- dsc_common_mode = DSC_MODE_SPLIT_PANEL;
+ dsc_common_mode = 0;
+ if (!half_panel_partial_update)
+ dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
if (enc_master->intf_mode == INTF_MODE_VIDEO)
dsc_common_mode |= DSC_MODE_VIDEO;
- SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
- pic_width, pic_height, dsc_common_mode);
- SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
- dsc_common_mode);
+ memcpy(&dsc[0], &sde_enc->disp_info.comp_info.dsc_info, sizeof(dsc[0]));
+ memcpy(&dsc[1], &sde_enc->disp_info.comp_info.dsc_info, sizeof(dsc[1]));
- _sde_encoder_dsc_pipe_cfg(l_hw_dsc, l_hw_pp, dsc, dsc_common_mode,
- ich_res);
- _sde_encoder_dsc_pipe_cfg(r_hw_dsc, r_hw_pp, dsc, dsc_common_mode,
- ich_res);
+ /*
+ * Since both DSC use same pic dimension, set same pic dimension
+ * to both DSC structures.
+ */
+ _sde_encoder_dsc_update_pic_dim(&dsc[0], roi->w, roi->h);
+ _sde_encoder_dsc_update_pic_dim(&dsc[1], roi->w, roi->h);
+
+ this_frame_slices = roi->w / dsc[0].slice_width;
+ intf_ip_w = this_frame_slices * dsc[0].slice_width;
+
+ if (!half_panel_partial_update)
+ intf_ip_w /= 2;
+
+ /*
+ * In this topology when both interfaces are active, they have same
+ * load so intf_ip_w will be same.
+ */
+ _sde_encoder_dsc_pclk_param_calc(&dsc[0], intf_ip_w);
+ _sde_encoder_dsc_pclk_param_calc(&dsc[1], intf_ip_w);
+
+ /*
+ * In this topology, since there is no dsc_merge, uncompressed input
+ * to encoder and interface is same.
+ */
+ enc_ip_w = intf_ip_w;
+ _sde_encoder_dsc_initial_line_calc(&dsc[0], enc_ip_w);
+ _sde_encoder_dsc_initial_line_calc(&dsc[1], enc_ip_w);
+
+ /*
+ * __is_ich_reset_override_needed should be called only after
+ * updating pic dimension, mdss_panel_dsc_update_pic_dim.
+ */
+ ich_res = _sde_encoder_dsc_ich_reset_override_needed(
+ half_panel_partial_update, &dsc[0]);
+
+ SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
+ roi->w, roi->h, dsc_common_mode);
+
+ for (i = 0; i < sde_enc->num_phys_encs; i++) {
+ bool active = !!((1 << i) & params->affected_displays);
+
+ SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h,
+ dsc_common_mode, i, active);
+ _sde_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], &dsc[i],
+ dsc_common_mode, ich_res, active);
+ }
return 0;
}
-static int _sde_encoder_dsc_2_lm_2_enc_1_intf(struct sde_encoder_virt *sde_enc)
+static int _sde_encoder_dsc_2_lm_2_enc_1_intf(struct sde_encoder_virt *sde_enc,
+ struct sde_encoder_kickoff_params *params)
{
- int pic_width, pic_height;
int this_frame_slices;
int intf_ip_w, enc_ip_w;
int ich_res, dsc_common_mode;
struct sde_encoder_phys *enc_master = sde_enc->cur_master;
- struct sde_hw_dsc *l_hw_dsc = sde_enc->hw_dsc[0];
- struct sde_hw_dsc *r_hw_dsc = sde_enc->hw_dsc[1];
- struct sde_hw_pingpong *l_hw_pp = sde_enc->hw_pp[0];
- struct sde_hw_pingpong *r_hw_pp = sde_enc->hw_pp[1];
- struct sde_hw_mdp *hw_mdp_top = enc_master->hw_mdptop;
+ const struct sde_rect *roi = &sde_enc->cur_conn_roi;
+ struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+ struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
struct msm_display_dsc_info *dsc =
&sde_enc->disp_info.comp_info.dsc_info;
+ bool half_panel_partial_update;
+ int i;
- if (l_hw_dsc == NULL || r_hw_dsc == NULL || hw_mdp_top == NULL ||
- l_hw_pp == NULL || r_hw_pp == NULL) {
- SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
- return -EINVAL;
+ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ hw_pp[i] = sde_enc->hw_pp[i];
+ hw_dsc[i] = sde_enc->hw_dsc[i];
+
+ if (!hw_pp[i] || !hw_dsc[i]) {
+ SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
+ return -EINVAL;
+ }
}
- pic_width = dsc->pic_width;
- pic_height = dsc->pic_height;
- _sde_encoder_dsc_update_pic_dim(dsc, pic_width, pic_height);
+ half_panel_partial_update =
+ hweight_long(params->affected_displays) == 1;
- this_frame_slices = pic_width / dsc->slice_width;
+ dsc_common_mode = 0;
+ if (!half_panel_partial_update)
+ dsc_common_mode |= DSC_MODE_SPLIT_PANEL | DSC_MODE_MULTIPLEX;
+ if (enc_master->intf_mode == INTF_MODE_VIDEO)
+ dsc_common_mode |= DSC_MODE_VIDEO;
+
+ _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h);
+
+ this_frame_slices = roi->w / dsc->slice_width;
intf_ip_w = this_frame_slices * dsc->slice_width;
_sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w);
/*
- * when using 2 encoders for the same stream, no. of slices
- * need to be same on both the encoders.
+ * dsc merge case: when using 2 encoders for the same stream,
+ * no. of slices need to be same on both the encoders.
*/
enc_ip_w = intf_ip_w / 2;
_sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
- ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc);
-
- dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
- if (enc_master->intf_mode == INTF_MODE_VIDEO)
- dsc_common_mode |= DSC_MODE_VIDEO;
+ ich_res = _sde_encoder_dsc_ich_reset_override_needed(
+ half_panel_partial_update, dsc);
SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n",
- pic_width, pic_height, dsc_common_mode);
- SDE_EVT32(DRMID(&sde_enc->base), pic_width, pic_height,
- dsc_common_mode);
+ roi->w, roi->h, dsc_common_mode);
+ SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h,
+ dsc_common_mode, i, params->affected_displays);
- _sde_encoder_dsc_pipe_cfg(l_hw_dsc, l_hw_pp, dsc, dsc_common_mode,
- ich_res);
- _sde_encoder_dsc_pipe_cfg(r_hw_dsc, r_hw_pp, dsc, dsc_common_mode,
- ich_res);
+ _sde_encoder_dsc_pipe_cfg(hw_dsc[0], hw_pp[0], dsc, dsc_common_mode,
+ ich_res, true);
+ _sde_encoder_dsc_pipe_cfg(hw_dsc[1], hw_pp[1], dsc, dsc_common_mode,
+ ich_res, !half_panel_partial_update);
return 0;
}
-static int _sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc)
+static int _sde_encoder_update_roi(struct drm_encoder *drm_enc)
+{
+ struct sde_encoder_virt *sde_enc;
+ struct drm_connector *drm_conn;
+ struct drm_display_mode *adj_mode;
+ struct sde_rect roi;
+
+ if (!drm_enc || !drm_enc->crtc || !drm_enc->crtc->state)
+ return -EINVAL;
+ sde_enc = to_sde_encoder_virt(drm_enc);
+
+ if (!sde_enc->cur_master)
+ return -EINVAL;
+
+ adj_mode = &sde_enc->base.crtc->state->adjusted_mode;
+ drm_conn = sde_enc->cur_master->connector;
+
+ _sde_encoder_get_connector_roi(sde_enc, &roi);
+ if (sde_kms_rect_is_null(&roi)) {
+ roi.w = adj_mode->hdisplay;
+ roi.h = adj_mode->vdisplay;
+ }
+
+ memcpy(&sde_enc->prv_conn_roi, &sde_enc->cur_conn_roi,
+ sizeof(sde_enc->prv_conn_roi));
+ memcpy(&sde_enc->cur_conn_roi, &roi, sizeof(sde_enc->cur_conn_roi));
+
+ return 0;
+}
+
+static int _sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc,
+ struct sde_encoder_kickoff_params *params)
{
enum sde_rm_topology_name topology;
- struct drm_connector *drm_conn = sde_enc->phys_encs[0]->connector;
+ struct drm_connector *drm_conn;
int ret = 0;
+ if (!sde_enc || !params || !sde_enc->phys_encs[0] ||
+ !sde_enc->phys_encs[0]->connector)
+ return -EINVAL;
+
+ drm_conn = sde_enc->phys_encs[0]->connector;
+
topology = sde_connector_get_topology_name(drm_conn);
if (topology == SDE_RM_TOPOLOGY_NONE) {
SDE_ERROR_ENC(sde_enc, "topology not set yet\n");
@@ -743,15 +894,19 @@
SDE_DEBUG_ENC(sde_enc, "\n");
SDE_EVT32(DRMID(&sde_enc->base));
+ if (sde_kms_rect_is_equal(&sde_enc->cur_conn_roi,
+ &sde_enc->prv_conn_roi))
+ return ret;
+
switch (topology) {
case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC:
ret = _sde_encoder_dsc_1_lm_1_enc_1_intf(sde_enc);
break;
case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE:
- ret = _sde_encoder_dsc_2_lm_2_enc_1_intf(sde_enc);
+ ret = _sde_encoder_dsc_2_lm_2_enc_1_intf(sde_enc, params);
break;
case SDE_RM_TOPOLOGY_DUALPIPE_DSC:
- ret = _sde_encoder_dsc_2_lm_2_enc_2_intf(sde_enc);
+ ret = _sde_encoder_dsc_2_lm_2_enc_2_intf(sde_enc, params);
break;
default:
SDE_ERROR_ENC(sde_enc, "No DSC support for topology %d",
@@ -1217,7 +1372,6 @@
struct sde_kms *sde_kms;
struct sde_hw_mdp *hw_mdptop;
int i = 0;
- int ret = 0;
struct sde_watchdog_te_status te_cfg = { 0 };
if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
@@ -1252,12 +1406,6 @@
sde_enc->cur_master->hw_mdptop,
sde_kms->catalog);
- if (_sde_is_dsc_enabled(sde_enc)) {
- ret = _sde_encoder_dsc_setup(sde_enc);
- if (ret)
- SDE_ERROR_ENC(sde_enc, "failed to setup DSC:%d\n", ret);
- }
-
if (hw_mdptop->ops.setup_vsync_sel) {
for (i = 0; i < sde_enc->num_phys_encs; i++)
te_cfg.ppnumber[i] = sde_enc->hw_pp[i]->idx;
@@ -1763,6 +1911,65 @@
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}
+static void _sde_encoder_ppsplit_swap_intf_for_right_only_update(
+ struct drm_encoder *drm_enc,
+ unsigned long *affected_displays,
+ int num_active_phys)
+{
+ struct sde_encoder_virt *sde_enc;
+ struct sde_encoder_phys *master;
+ enum sde_rm_topology_name topology;
+ bool is_right_only;
+
+ if (!drm_enc || !affected_displays)
+ return;
+
+ sde_enc = to_sde_encoder_virt(drm_enc);
+ master = sde_enc->cur_master;
+ if (!master || !master->connector)
+ return;
+
+ topology = sde_connector_get_topology_name(master->connector);
+ if (topology != SDE_RM_TOPOLOGY_PPSPLIT)
+ return;
+
+ /*
+ * For pingpong split, the slave pingpong won't generate IRQs. For
+ * right-only updates, we can't swap pingpongs, or simply swap the
+ * master/slave assignment, we actually have to swap the interfaces
+ * so that the master physical encoder will use a pingpong/interface
+ * that generates irqs on which to wait.
+ */
+ is_right_only = !test_bit(0, affected_displays) &&
+ test_bit(1, affected_displays);
+
+ if (is_right_only && !sde_enc->intfs_swapped) {
+ /* right-only update swap interfaces */
+ swap(sde_enc->phys_encs[0]->intf_idx,
+ sde_enc->phys_encs[1]->intf_idx);
+ sde_enc->intfs_swapped = true;
+ } else if (!is_right_only && sde_enc->intfs_swapped) {
+ /* left-only or full update, swap back */
+ swap(sde_enc->phys_encs[0]->intf_idx,
+ sde_enc->phys_encs[1]->intf_idx);
+ sde_enc->intfs_swapped = false;
+ }
+
+ SDE_DEBUG_ENC(sde_enc,
+ "right_only %d swapped %d phys0->intf%d, phys1->intf%d\n",
+ is_right_only, sde_enc->intfs_swapped,
+ sde_enc->phys_encs[0]->intf_idx - INTF_0,
+ sde_enc->phys_encs[1]->intf_idx - INTF_0);
+ SDE_EVT32(DRMID(drm_enc), is_right_only, sde_enc->intfs_swapped,
+ sde_enc->phys_encs[0]->intf_idx - INTF_0,
+ sde_enc->phys_encs[1]->intf_idx - INTF_0,
+ *affected_displays);
+
+ /* ppsplit always uses master since ppslave invalid for irqs*/
+ if (num_active_phys == 1)
+ *affected_displays = BIT(0);
+}
+
static void _sde_encoder_update_master(struct drm_encoder *drm_enc,
struct sde_encoder_kickoff_params *params)
{
@@ -1785,6 +1992,10 @@
SDE_DEBUG_ENC(sde_enc, "affected_displays 0x%lx num_active_phys %d\n",
params->affected_displays, num_active_phys);
+ /* for left/right only update, ppsplit master switches interface */
+ _sde_encoder_ppsplit_swap_intf_for_right_only_update(drm_enc,
+ ¶ms->affected_displays, num_active_phys);
+
for (i = 0; i < sde_enc->num_phys_encs; i++) {
enum sde_enc_split_role prv_role, new_role;
bool active;
@@ -1814,6 +2025,9 @@
SDE_DEBUG_ENC(sde_enc, "pp %d role prv %d new %d active %d\n",
phys->hw_pp->idx - PINGPONG_0, prv_role,
phys->split_role, active);
+ SDE_EVT32(DRMID(drm_enc), params->affected_displays,
+ phys->hw_pp->idx - PINGPONG_0, prv_role,
+ phys->split_role, active, num_active_phys);
}
}
@@ -1892,6 +2106,8 @@
_sde_encoder_update_master(drm_enc, params);
+ _sde_encoder_update_roi(drm_enc);
+
if (sde_enc->cur_master && sde_enc->cur_master->connector) {
rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
if (rc)
@@ -1899,6 +2115,12 @@
sde_enc->cur_master->connector->base.id,
rc);
}
+
+ if (sde_encoder_is_dsc_enabled(drm_enc)) {
+ rc = _sde_encoder_dsc_setup(sde_enc, params);
+ if (rc)
+ SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc);
+ }
}
void sde_encoder_kickoff(struct drm_encoder *drm_enc)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 7292a12..6ef245b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -149,6 +149,20 @@
void sde_encoder_virt_restore(struct drm_encoder *encoder);
/**
+ * sde_encoder_is_dsc_enabled - check if encoder is in DSC mode
+ * @drm_enc: Pointer to drm encoder object
+ * @Return: true if encoder is in DSC mode
+ */
+bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc);
+
+/**
+ * sde_encoder_is_dsc_merge - check if encoder is in DSC merge mode
+ * @drm_enc: Pointer to drm encoder object
+ * @Return: true if encoder is in DSC merge mode
+ */
+bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc);
+
+/**
* sde_encoder_init - initialize virtual encoder object
* @dev: Pointer to drm device structure
* @disp_info: Pointer to display information structure
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 572bd9e..7adab09 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -615,7 +615,8 @@
phys_enc->hw_pp->idx - PINGPONG_0);
drm_mode_debug_printmodeline(&phys_enc->cached_mode);
- _sde_encoder_phys_cmd_update_intf_cfg(phys_enc);
+ if (!_sde_encoder_phys_is_ppsplit_slave(phys_enc))
+ _sde_encoder_phys_cmd_update_intf_cfg(phys_enc);
sde_encoder_phys_cmd_tearcheck_config(phys_enc);
}
@@ -699,6 +700,8 @@
}
}
+ if (phys_enc->hw_pp->ops.enable_tearcheck)
+ phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, false);
phys_enc->enable_state = SDE_ENC_DISABLED;
}
@@ -832,15 +835,28 @@
struct sde_encoder_phys *phys_enc,
enum sde_enc_split_role role)
{
- struct sde_encoder_phys_cmd *cmd_enc =
- to_sde_encoder_phys_cmd(phys_enc);
- enum sde_enc_split_role old_role = phys_enc->split_role;
+ struct sde_encoder_phys_cmd *cmd_enc;
+ enum sde_enc_split_role old_role;
+ bool is_ppsplit;
+
+ if (!phys_enc)
+ return;
+
+ cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
+ old_role = phys_enc->split_role;
+ is_ppsplit = _sde_encoder_phys_is_ppsplit(phys_enc);
+
+ phys_enc->split_role = role;
SDE_DEBUG_CMDENC(cmd_enc, "old role %d new role %d\n",
old_role, role);
- phys_enc->split_role = role;
- if (role == ENC_ROLE_SKIP || role == old_role)
+ /*
+ * ppsplit solo needs to reprogram because intf may have swapped without
+ * role changing on left-only, right-only back-to-back commits
+ */
+ if (!(is_ppsplit && role == ENC_ROLE_SOLO) &&
+ (role == old_role || role == ENC_ROLE_SKIP))
return;
sde_encoder_helper_split_config(phys_enc, phys_enc->intf_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 28a2b16..385c610 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -21,6 +21,7 @@
#include "sde_core_irq.h"
#include "sde_wb.h"
#include "sde_vbif.h"
+#include "sde_crtc.h"
#define to_sde_encoder_phys_wb(x) \
container_of(x, struct sde_encoder_phys_wb, base)
@@ -104,6 +105,48 @@
}
/**
+ * sde_encoder_phys_wb_set_qos_remap - set QoS remapper for writeback
+ * @phys_enc: Pointer to physical encoder
+ */
+static void sde_encoder_phys_wb_set_qos_remap(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_wb *wb_enc;
+ struct sde_hw_wb *hw_wb;
+ struct drm_crtc *crtc;
+ struct sde_vbif_set_qos_params qos_params;
+
+ if (!phys_enc || !phys_enc->parent || !phys_enc->parent->crtc) {
+ SDE_ERROR("invalid arguments\n");
+ return;
+ }
+
+ wb_enc = to_sde_encoder_phys_wb(phys_enc);
+ crtc = phys_enc->parent->crtc;
+
+ if (!wb_enc->hw_wb || !wb_enc->hw_wb->caps) {
+ SDE_ERROR("invalid writeback hardware\n");
+ return;
+ }
+
+ hw_wb = wb_enc->hw_wb;
+
+ memset(&qos_params, 0, sizeof(qos_params));
+ qos_params.vbif_idx = hw_wb->caps->vbif_idx;
+ qos_params.xin_id = hw_wb->caps->xin_id;
+ qos_params.clk_ctrl = hw_wb->caps->clk_ctrl;
+ qos_params.num = hw_wb->idx - WB_0;
+ qos_params.is_rt = sde_crtc_get_client_type(crtc) != NRT_CLIENT;
+
+ SDE_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d rt:%d\n",
+ qos_params.num,
+ qos_params.vbif_idx,
+ qos_params.xin_id, qos_params.is_rt);
+
+ sde_vbif_set_qos_remap(phys_enc->sde_kms, &qos_params);
+}
+
+/**
* sde_encoder_phys_setup_cdm - setup chroma down block
* @phys_enc: Pointer to physical encoder
* @fb: Pointer to output framebuffer
@@ -528,6 +571,8 @@
sde_encoder_phys_wb_set_traffic_shaper(phys_enc);
+ sde_encoder_phys_wb_set_qos_remap(phys_enc);
+
sde_encoder_phys_setup_cdm(phys_enc, fb, wb_enc->wb_fmt, wb_roi);
sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 1faa46e2..30e63da 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -285,6 +285,8 @@
VBIF_DEFAULT_OT_WR_LIMIT,
VBIF_DYNAMIC_OT_RD_LIMIT,
VBIF_DYNAMIC_OT_WR_LIMIT,
+ VBIF_QOS_RT_REMAP,
+ VBIF_QOS_NRT_REMAP,
VBIF_PROP_MAX,
};
@@ -512,6 +514,10 @@
PROP_TYPE_U32_ARRAY},
{VBIF_DYNAMIC_OT_WR_LIMIT, "qcom,sde-vbif-dynamic-ot-wr-limit", false,
PROP_TYPE_U32_ARRAY},
+ {VBIF_QOS_RT_REMAP, "qcom,sde-vbif-qos-rt-remap", false,
+ PROP_TYPE_U32_ARRAY},
+ {VBIF_QOS_NRT_REMAP, "qcom,sde-vbif-qos-nrt-remap", false,
+ PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type reg_dma_prop[REG_DMA_PROP_MAX] = {
@@ -1049,6 +1055,13 @@
set_bit(SDE_SSPP_SRC, &sspp->features);
+ if (sde_cfg->ts_prefill_rev == 1) {
+ set_bit(SDE_SSPP_TS_PREFILL, &sspp->features);
+ } else if (sde_cfg->ts_prefill_rev == 2) {
+ set_bit(SDE_SSPP_TS_PREFILL, &sspp->features);
+ set_bit(SDE_SSPP_TS_PREFILL_REC1, &sspp->features);
+ }
+
sblk->smart_dma_priority =
PROP_VALUE_ACCESS(prop_value, SSPP_SMART_DMA, i);
@@ -1926,7 +1939,7 @@
int rc, prop_count[VBIF_PROP_MAX], i, j, k;
struct sde_prop_value *prop_value = NULL;
bool prop_exists[VBIF_PROP_MAX];
- u32 off_count, vbif_len, rd_len = 0, wr_len = 0;
+ u32 off_count, vbif_len;
struct sde_vbif_cfg *vbif;
if (!sde_cfg) {
@@ -1948,12 +1961,22 @@
goto end;
rc = _validate_dt_entry(np, &vbif_prop[VBIF_DYNAMIC_OT_RD_LIMIT], 1,
- &prop_count[VBIF_DYNAMIC_OT_RD_LIMIT], &rd_len);
+ &prop_count[VBIF_DYNAMIC_OT_RD_LIMIT], NULL);
if (rc)
goto end;
rc = _validate_dt_entry(np, &vbif_prop[VBIF_DYNAMIC_OT_WR_LIMIT], 1,
- &prop_count[VBIF_DYNAMIC_OT_WR_LIMIT], &wr_len);
+ &prop_count[VBIF_DYNAMIC_OT_WR_LIMIT], NULL);
+ if (rc)
+ goto end;
+
+ rc = _validate_dt_entry(np, &vbif_prop[VBIF_QOS_RT_REMAP], 1,
+ &prop_count[VBIF_QOS_RT_REMAP], NULL);
+ if (rc)
+ goto end;
+
+ rc = _validate_dt_entry(np, &vbif_prop[VBIF_QOS_NRT_REMAP], 1,
+ &prop_count[VBIF_QOS_NRT_REMAP], NULL);
if (rc)
goto end;
@@ -2048,6 +2071,63 @@
vbif->dynamic_ot_rd_tbl.count ||
vbif->dynamic_ot_wr_tbl.count)
set_bit(SDE_VBIF_QOS_OTLIM, &vbif->features);
+
+ vbif->qos_rt_tbl.npriority_lvl =
+ prop_count[VBIF_QOS_RT_REMAP];
+ SDE_DEBUG("qos_rt_tbl.npriority_lvl=%u\n",
+ vbif->qos_rt_tbl.npriority_lvl);
+ if (vbif->qos_rt_tbl.npriority_lvl == sde_cfg->vbif_qos_nlvl) {
+ vbif->qos_rt_tbl.priority_lvl = kcalloc(
+ vbif->qos_rt_tbl.npriority_lvl, sizeof(u32),
+ GFP_KERNEL);
+ if (!vbif->qos_rt_tbl.priority_lvl) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ } else if (vbif->qos_rt_tbl.npriority_lvl) {
+ vbif->qos_rt_tbl.npriority_lvl = 0;
+ vbif->qos_rt_tbl.priority_lvl = NULL;
+ SDE_ERROR("invalid qos rt table\n");
+ }
+
+ for (j = 0; j < vbif->qos_rt_tbl.npriority_lvl; j++) {
+ vbif->qos_rt_tbl.priority_lvl[j] =
+ PROP_VALUE_ACCESS(prop_value,
+ VBIF_QOS_RT_REMAP, j);
+ SDE_DEBUG("lvl[%d]=%u\n", j,
+ vbif->qos_rt_tbl.priority_lvl[j]);
+ }
+
+ vbif->qos_nrt_tbl.npriority_lvl =
+ prop_count[VBIF_QOS_NRT_REMAP];
+ SDE_DEBUG("qos_nrt_tbl.npriority_lvl=%u\n",
+ vbif->qos_nrt_tbl.npriority_lvl);
+
+ if (vbif->qos_nrt_tbl.npriority_lvl == sde_cfg->vbif_qos_nlvl) {
+ vbif->qos_nrt_tbl.priority_lvl = kcalloc(
+ vbif->qos_nrt_tbl.npriority_lvl, sizeof(u32),
+ GFP_KERNEL);
+ if (!vbif->qos_nrt_tbl.priority_lvl) {
+ rc = -ENOMEM;
+ goto end;
+ }
+ } else if (vbif->qos_nrt_tbl.npriority_lvl) {
+ vbif->qos_nrt_tbl.npriority_lvl = 0;
+ vbif->qos_nrt_tbl.priority_lvl = NULL;
+ SDE_ERROR("invalid qos nrt table\n");
+ }
+
+ for (j = 0; j < vbif->qos_nrt_tbl.npriority_lvl; j++) {
+ vbif->qos_nrt_tbl.priority_lvl[j] =
+ PROP_VALUE_ACCESS(prop_value,
+ VBIF_QOS_NRT_REMAP, j);
+ SDE_DEBUG("lvl[%d]=%u\n", j,
+ vbif->qos_nrt_tbl.priority_lvl[j]);
+ }
+
+ if (vbif->qos_rt_tbl.npriority_lvl ||
+ vbif->qos_nrt_tbl.npriority_lvl)
+ set_bit(SDE_VBIF_QOS_REMAP, &vbif->features);
}
end:
@@ -2510,11 +2590,17 @@
/* update msm8998 target here */
sde_cfg->has_wb_ubwc = true;
sde_cfg->perf.min_prefill_lines = 25;
+ sde_cfg->vbif_qos_nlvl = 4;
+ sde_cfg->ts_prefill_rev = 1;
+ sde_cfg->perf.min_prefill_lines = 25;
break;
case SDE_HW_VER_400:
- /* update msm8998 and sdm845 target here */
+ /* update sdm845 target here */
sde_cfg->has_wb_ubwc = true;
sde_cfg->perf.min_prefill_lines = 24;
+ sde_cfg->vbif_qos_nlvl = 8;
+ sde_cfg->ts_prefill_rev = 2;
+ sde_cfg->perf.min_prefill_lines = 24;
break;
default:
sde_cfg->perf.min_prefill_lines = 0xffff;
@@ -2549,6 +2635,8 @@
for (i = 0; i < sde_cfg->vbif_count; i++) {
kfree(sde_cfg->vbif[i].dynamic_ot_rd_tbl.cfg);
kfree(sde_cfg->vbif[i].dynamic_ot_wr_tbl.cfg);
+ kfree(sde_cfg->vbif[i].qos_rt_tbl.priority_lvl);
+ kfree(sde_cfg->vbif[i].qos_nrt_tbl.priority_lvl);
}
kfree(sde_cfg->dma_formats);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index cfb1b67..e24192b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -111,6 +111,8 @@
* @SDE_SSPP_SMART_DMA_V1, SmartDMA 1.0 support
* @SDE_SSPP_SMART_DMA_V2, SmartDMA 2.0 support
* @SDE_SSPP_SBUF, SSPP support inline stream buffer
+ * @SDE_SSPP_TS_PREFILL Supports prefill with traffic shaper
+ * @SDE_SSPP_TS_PREFILL_REC1 Supports prefill with traffic shaper multirec
* @SDE_SSPP_MAX maximum value
*/
enum {
@@ -130,6 +132,8 @@
SDE_SSPP_SMART_DMA_V1,
SDE_SSPP_SMART_DMA_V2,
SDE_SSPP_SBUF,
+ SDE_SSPP_TS_PREFILL,
+ SDE_SSPP_TS_PREFILL_REC1,
SDE_SSPP_MAX
};
@@ -258,10 +262,12 @@
/**
* VBIF sub-blocks and features
* @SDE_VBIF_QOS_OTLIM VBIF supports OT Limit
+ * @SDE_VBIF_QOS_REMAP VBIF supports QoS priority remap
* @SDE_VBIF_MAX maximum value
*/
enum {
SDE_VBIF_QOS_OTLIM = 0x1,
+ SDE_VBIF_QOS_REMAP,
SDE_VBIF_MAX
};
@@ -653,6 +659,16 @@
};
/**
+ * struct sde_vbif_qos_tbl - QoS priority table
+ * @npriority_lvl num of priority level
+ * @priority_lvl pointer to array of priority level in ascending order
+ */
+struct sde_vbif_qos_tbl {
+ u32 npriority_lvl;
+ u32 *priority_lvl;
+};
+
+/**
* struct sde_vbif_cfg - information of VBIF blocks
* @id enum identifying this block
* @base register offset of this block
@@ -662,6 +678,8 @@
* @xin_halt_timeout maximum time (in usec) for xin to halt
* @dynamic_ot_rd_tbl dynamic OT read configuration table
* @dynamic_ot_wr_tbl dynamic OT write configuration table
+ * @qos_rt_tbl real-time QoS priority table
+ * @qos_nrt_tbl non-real-time QoS priority table
*/
struct sde_vbif_cfg {
SDE_HW_BLK_INFO;
@@ -670,6 +688,8 @@
u32 xin_halt_timeout;
struct sde_vbif_dynamic_ot_tbl dynamic_ot_rd_tbl;
struct sde_vbif_dynamic_ot_tbl dynamic_ot_wr_tbl;
+ struct sde_vbif_qos_tbl qos_rt_tbl;
+ struct sde_vbif_qos_tbl qos_nrt_tbl;
};
/**
* struct sde_reg_dma_cfg - information of lut dma blocks
@@ -746,6 +766,8 @@
* @cursor_formats Supported formats for cursor pipe
* @vig_formats Supported formats for vig pipe
* @wb_formats Supported formats for wb
+ * @vbif_qos_nlvl number of vbif QoS priority level
+ * @ts_prefill_rev prefill traffic shaper feature revision
*/
struct sde_mdss_cfg {
u32 hwversion;
@@ -765,6 +787,8 @@
bool has_sbuf;
u32 sbuf_headroom;
bool has_idle_pc;
+ u32 vbif_qos_nlvl;
+ u32 ts_prefill_rev;
u32 mdss_count;
struct sde_mdss_base_cfg mdss[MAX_BLOCKS];
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 7780c5b..fedc72c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -71,7 +71,7 @@
if (stage == SDE_STAGE_BASE)
rc = -EINVAL;
else if (stage <= sblk->maxblendstages)
- rc = sblk->blendstage_base[stage - 1];
+ rc = sblk->blendstage_base[stage - SDE_STAGE_0];
else
rc = -EINVAL;
@@ -198,7 +198,7 @@
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
int stage_off;
- u32 val = 0;
+ u32 val = 0, alpha = 0;
stage_off = _stage_offset(ctx, dim_layer->stage);
if (stage_off < 0) {
@@ -206,13 +206,13 @@
return;
}
- val = (dim_layer->color_fill.color_1 & 0xFFF) << 16 |
- (dim_layer->color_fill.color_0 & 0xFFF);
+ alpha = dim_layer->color_fill.color_3 & 0xFF;
+ val = ((dim_layer->color_fill.color_1 << 2) & 0xFFF) << 16 |
+ ((dim_layer->color_fill.color_0 << 2) & 0xFFF);
SDE_REG_WRITE(c, LM_FG_COLOR_FILL_COLOR_0 + stage_off, val);
- val = 0;
- val = (dim_layer->color_fill.color_3 & 0xFFF) << 16 |
- (dim_layer->color_fill.color_2 & 0xFFF);
+ val = (alpha << 4) << 16 |
+ ((dim_layer->color_fill.color_2 << 2) & 0xFFF);
SDE_REG_WRITE(c, LM_FG_COLOR_FILL_COLOR_1 + stage_off, val);
val = dim_layer->rect.h << 16 | dim_layer->rect.w;
@@ -222,9 +222,14 @@
SDE_REG_WRITE(c, LM_FG_COLOR_FILL_XY + stage_off, val);
val = BIT(16); /* enable dim layer */
+ val |= SDE_BLEND_FG_ALPHA_FG_CONST | SDE_BLEND_BG_ALPHA_BG_CONST;
if (dim_layer->flags & SDE_DRM_DIM_LAYER_EXCLUSIVE)
val |= BIT(17);
+ else
+ val &= ~BIT(17);
SDE_REG_WRITE(c, LM_BLEND0_OP + stage_off, val);
+ val = (alpha << 16) | (0xff - alpha);
+ SDE_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, val);
}
static void sde_hw_lm_setup_misr(struct sde_hw_mixer *ctx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.c b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
index d15b804..d5f03a6a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
@@ -563,6 +563,10 @@
case SDE_HW_ROT_CMD_COMMIT:
cmd_type = SDE_ROTATOR_INLINE_CMD_COMMIT;
break;
+ case SDE_HW_ROT_CMD_START:
+ cmd_type = SDE_ROTATOR_INLINE_CMD_START;
+ priv_handle = data->priv_handle;
+ break;
case SDE_HW_ROT_CMD_CLEANUP:
cmd_type = SDE_ROTATOR_INLINE_CMD_CLEANUP;
priv_handle = data->priv_handle;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.h b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
index a4f5b49..e490052 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
@@ -24,11 +24,13 @@
* enum sde_hw_rot_cmd_type - type of rotator hardware command
* @SDE_HW_ROT_CMD_VALDIATE: validate rotator command; do not commit
* @SDE_HW_ROT_CMD_COMMIT: commit/execute rotator command
+ * @SDE_HW_ROT_CMD_START: mdp is ready to start
* @SDE_HW_ROT_CMD_CLEANUP: cleanup rotator command after it is done
*/
enum sde_hw_rot_cmd_type {
SDE_HW_ROT_CMD_VALIDATE,
SDE_HW_ROT_CMD_COMMIT,
+ SDE_HW_ROT_CMD_START,
SDE_HW_ROT_CMD_CLEANUP,
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index a1f5cee..694d267 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -79,11 +79,16 @@
#define SSPP_SW_PIX_EXT_C3_LR 0x120
#define SSPP_SW_PIX_EXT_C3_TB 0x124
#define SSPP_SW_PIX_EXT_C3_REQ_PIXELS 0x128
+#define SSPP_TRAFFIC_SHAPER 0x130
#define SSPP_UBWC_ERROR_STATUS 0x138
+#define SSPP_TRAFFIC_SHAPER_PREFILL 0x150
+#define SSPP_TRAFFIC_SHAPER_REC1_PREFILL 0x154
+#define SSPP_TRAFFIC_SHAPER_REC1 0x158
#define SSPP_EXCL_REC_SIZE 0x1B4
#define SSPP_EXCL_REC_XY 0x1B8
#define SSPP_VIG_OP_MODE 0x0
#define SSPP_VIG_CSC_10_OP_MODE 0x0
+#define SSPP_TRAFFIC_SHAPER_BPC_MAX 0xFF
/* SSPP_QOS_CTRL */
#define SSPP_QOS_CTRL_VBLANK_EN BIT(16)
@@ -186,6 +191,9 @@
#define VIG_CSC_10_EN BIT(0)
#define CSC_10BIT_OFFSET 4
+/* traffic shaper clock in Hz */
+#define TS_CLK 19200000
+
static inline int _sspp_subblk_offset(struct sde_hw_pipe *ctx,
int s_id,
u32 *idx)
@@ -1041,6 +1049,51 @@
status->rd_ptr[1] = val & 0xffff;
}
+static void sde_hw_sspp_setup_ts_prefill(struct sde_hw_pipe *ctx,
+ struct sde_hw_pipe_ts_cfg *cfg,
+ enum sde_sspp_multirect_index index)
+{
+ u32 idx;
+ u32 ts_offset, ts_prefill_offset;
+ u32 ts_count = 0, ts_bytes = 0;
+ const struct sde_sspp_cfg *cap;
+
+ if (!ctx || !cfg || !ctx->cap)
+ return;
+
+ if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+ return;
+
+ cap = ctx->cap;
+
+ if (index == SDE_SSPP_RECT_0 &&
+ test_bit(SDE_SSPP_TS_PREFILL, &cap->features)) {
+ ts_offset = SSPP_TRAFFIC_SHAPER;
+ ts_prefill_offset = SSPP_TRAFFIC_SHAPER_PREFILL;
+ } else if (index == SDE_SSPP_RECT_1 &&
+ test_bit(SDE_SSPP_TS_PREFILL_REC1, &cap->features)) {
+ ts_offset = SSPP_TRAFFIC_SHAPER_REC1;
+ ts_prefill_offset = SSPP_TRAFFIC_SHAPER_REC1_PREFILL;
+ } else {
+ return;
+ }
+
+ if (cfg->time) {
+ ts_bytes = mult_frac(TS_CLK * 1000000ULL, cfg->size,
+ cfg->time);
+ if (ts_bytes > SSPP_TRAFFIC_SHAPER_BPC_MAX)
+ ts_bytes = SSPP_TRAFFIC_SHAPER_BPC_MAX;
+ }
+
+ if (ts_bytes) {
+ ts_count = DIV_ROUND_UP_ULL(cfg->size, ts_bytes);
+ ts_bytes |= BIT(31) | BIT(27);
+ }
+
+ SDE_REG_WRITE(&ctx->hw, ts_offset, ts_bytes);
+ SDE_REG_WRITE(&ctx->hw, ts_prefill_offset, ts_count);
+}
+
static void _setup_layer_ops(struct sde_hw_pipe *c,
unsigned long features)
{
@@ -1062,6 +1115,9 @@
c->ops.setup_qos_ctrl = sde_hw_sspp_setup_qos_ctrl;
}
+ if (test_bit(SDE_SSPP_TS_PREFILL, &features))
+ c->ops.setup_ts_prefill = sde_hw_sspp_setup_ts_prefill;
+
if (test_bit(SDE_SSPP_CSC, &features) ||
test_bit(SDE_SSPP_CSC_10BIT, &features))
c->ops.setup_csc = sde_hw_sspp_setup_csc;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 1b81e54..010b363 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -343,6 +343,16 @@
};
/**
+ * struct sde_hw_pipe_ts_cfg - traffic shaper configuration
+ * @size: size to prefill in bytes, or zero to disable
+ * @time: time to prefill in usec, or zero to disable
+ */
+struct sde_hw_pipe_ts_cfg {
+ u64 size;
+ u64 time;
+};
+
+/**
* Maximum number of stream buffer plane
*/
#define SDE_PIPE_SBUF_PLANE_NUM 2
@@ -554,6 +564,16 @@
*/
void (*get_sbuf_status)(struct sde_hw_pipe *ctx,
struct sde_hw_pipe_sbuf_status *status);
+
+ /**
+ * setup_ts_prefill - setup prefill traffic shaper
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to traffic shaper configuration
+ * @index: rectangle index in multirect
+ */
+ void (*setup_ts_prefill)(struct sde_hw_pipe *ctx,
+ struct sde_hw_pipe_ts_cfg *cfg,
+ enum sde_sspp_multirect_index index);
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index 048ec47..9b9763a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -33,6 +33,8 @@
#define VBIF_OUT_WR_LIM_CONF0 0x00D4
#define VBIF_XIN_HALT_CTRL0 0x0200
#define VBIF_XIN_HALT_CTRL1 0x0204
+#define VBIF_XINL_QOS_RP_REMAP_000 0x0550
+#define VBIF_XINL_QOS_LVL_REMAP_000 0x0590
static void sde_hw_set_limit_conf(struct sde_hw_vbif *vbif,
u32 xin_id, bool rd, u32 limit)
@@ -104,6 +106,35 @@
return (reg_val & BIT(xin_id)) ? true : false;
}
+static void sde_hw_set_qos_remap(struct sde_hw_vbif *vbif,
+ u32 xin_id, u32 level, u32 remap_level)
+{
+ struct sde_hw_blk_reg_map *c;
+ u32 reg_val, reg_val_lvl, mask, reg_high, reg_shift;
+
+ if (!vbif)
+ return;
+
+ c = &vbif->hw;
+
+ reg_high = ((xin_id & 0x8) >> 3) * 4 + (level * 8);
+ reg_shift = (xin_id & 0x7) * 4;
+
+ reg_val = SDE_REG_READ(c, VBIF_XINL_QOS_RP_REMAP_000 + reg_high);
+ reg_val_lvl = SDE_REG_READ(c, VBIF_XINL_QOS_LVL_REMAP_000 + reg_high);
+
+ mask = 0x7 << reg_shift;
+
+ reg_val &= ~mask;
+ reg_val |= (remap_level << reg_shift) & mask;
+
+ reg_val_lvl &= ~mask;
+ reg_val_lvl |= (remap_level << reg_shift) & mask;
+
+ SDE_REG_WRITE(c, VBIF_XINL_QOS_RP_REMAP_000 + reg_high, reg_val);
+ SDE_REG_WRITE(c, VBIF_XINL_QOS_LVL_REMAP_000 + reg_high, reg_val_lvl);
+}
+
static void _setup_vbif_ops(struct sde_hw_vbif_ops *ops,
unsigned long cap)
{
@@ -111,6 +142,8 @@
ops->get_limit_conf = sde_hw_get_limit_conf;
ops->set_halt_ctrl = sde_hw_set_halt_ctrl;
ops->get_halt_ctrl = sde_hw_get_halt_ctrl;
+ if (test_bit(SDE_VBIF_QOS_REMAP, &cap))
+ ops->set_qos_remap = sde_hw_set_qos_remap;
}
static const struct sde_vbif_cfg *_top_offset(enum sde_vbif vbif,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.h b/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
index de7fac0..c67738b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
@@ -61,6 +61,16 @@
*/
bool (*get_halt_ctrl)(struct sde_hw_vbif *vbif,
u32 xin_id);
+
+ /**
+ * set_qos_remap - set QoS priority remap
+ * @vbif: vbif context driver
+ * @xin_id: client interface identifier
+ * @level: priority level
+ * @remap_level: remapped level
+ */
+ void (*set_qos_remap)(struct sde_hw_vbif *vbif,
+ u32 xin_id, u32 level, u32 remap_level);
};
struct sde_hw_vbif {
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index e3b658a..eeb7a00 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
@@ -49,90 +49,14 @@
return IRQ_HANDLED;
}
-static void sde_hw_irq_mask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- /* memory barrier */
- smp_mb__before_atomic();
- clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- /* memory barrier */
- smp_mb__after_atomic();
-}
-
-static void sde_hw_irq_unmask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- /* memory barrier */
- smp_mb__before_atomic();
- set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- /* memory barrier */
- smp_mb__after_atomic();
-}
-
-static struct irq_chip sde_hw_irq_chip = {
- .name = "sde",
- .irq_mask = sde_hw_irq_mask,
- .irq_unmask = sde_hw_irq_unmask,
-};
-
-static int sde_hw_irqdomain_map(struct irq_domain *domain,
- unsigned int irq, irq_hw_number_t hwirq)
-{
- struct sde_kms *sde_kms;
- int rc;
-
- if (!domain || !domain->host_data) {
- SDE_ERROR("invalid parameters domain %d\n", domain != 0);
- return -EINVAL;
- }
- sde_kms = domain->host_data;
-
- irq_set_chip_and_handler(irq, &sde_hw_irq_chip, handle_level_irq);
- rc = irq_set_chip_data(irq, sde_kms);
-
- return rc;
-}
-
-static const struct irq_domain_ops sde_hw_irqdomain_ops = {
- .map = sde_hw_irqdomain_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
void sde_irq_preinstall(struct msm_kms *kms)
{
struct sde_kms *sde_kms = to_sde_kms(kms);
- struct device *dev;
- struct irq_domain *domain;
if (!sde_kms->dev || !sde_kms->dev->dev) {
pr_err("invalid device handles\n");
return;
}
- dev = sde_kms->dev->dev;
-
- domain = irq_domain_add_linear(dev->of_node, 32,
- &sde_hw_irqdomain_ops, sde_kms);
- if (!domain) {
- pr_err("failed to add irq_domain\n");
- return;
- }
-
- sde_kms->irq_controller.enabled_mask = 0;
- sde_kms->irq_controller.domain = domain;
sde_core_irq_preinstall(sde_kms);
}
@@ -162,9 +86,5 @@
}
sde_core_irq_uninstall(sde_kms);
-
- if (sde_kms->irq_controller.domain) {
- irq_domain_remove(sde_kms->irq_controller.domain);
- sde_kms->irq_controller.domain = NULL;
- }
+ sde_core_irq_domain_fini(sde_kms);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 8cc196a..303062f 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -25,6 +25,7 @@
#include "msm_drv.h"
#include "msm_mmu.h"
+#include "msm_gem.h"
#include "dsi_display.h"
#include "dsi_drm.h"
@@ -744,6 +745,9 @@
priv = dev->dev_private;
catalog = sde_kms->catalog;
+ ret = sde_core_irq_domain_add(sde_kms);
+ if (ret)
+ goto fail_irq;
/*
* Query for underlying display drivers, and create connectors,
* bridges and encoders for them.
@@ -821,6 +825,8 @@
return 0;
fail:
_sde_kms_drm_obj_destroy(sde_kms);
+fail_irq:
+ sde_core_irq_domain_fini(sde_kms);
return ret;
}
@@ -950,6 +956,13 @@
}
}
+static void sde_kms_set_gem_flags(struct msm_gem_object *msm_obj,
+ uint32_t flags)
+{
+ if (msm_obj)
+ msm_obj->flags |= flags;
+}
+
struct sde_kms_fbo *sde_kms_fbo_alloc(struct drm_device *dev, u32 width,
u32 height, u32 pixel_format, u64 modifier[4], u32 flags)
{
@@ -1036,10 +1049,13 @@
fbo->bo[0] = NULL;
goto done;
}
+
+ /* insert extra bo flags */
+ sde_kms_set_gem_flags(to_msm_bo(fbo->bo[0]), MSM_BO_KEEPATTRS);
} else {
mutex_lock(&dev->struct_mutex);
fbo->bo[0] = msm_gem_new(dev, fbo->layout.total_size,
- MSM_BO_SCANOUT | MSM_BO_WC);
+ MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_KEEPATTRS);
if (IS_ERR(fbo->bo[0])) {
mutex_unlock(&dev->struct_mutex);
SDE_ERROR("failed to new gem buffer\n");
@@ -1539,6 +1555,14 @@
goto perf_err;
}
+ sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
+ if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
+ rc = PTR_ERR(sde_kms->hw_intr);
+ SDE_ERROR("hw_intr init failed: %d\n", rc);
+ sde_kms->hw_intr = NULL;
+ goto hw_intr_init_err;
+ }
+
/*
* _sde_kms_drm_obj_init should create the DRM related objects
* i.e. CRTCs, planes, encoders, connectors and so forth
@@ -1564,23 +1588,12 @@
*/
dev->mode_config.allow_fb_modifiers = true;
- sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
- if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
- rc = PTR_ERR(sde_kms->hw_intr);
- if (!sde_kms->hw_intr)
- rc = -EINVAL;
- SDE_ERROR("hw_intr init failed: %d\n", rc);
- sde_kms->hw_intr = NULL;
- goto hw_intr_init_err;
- }
-
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
return 0;
-hw_intr_init_err:
- _sde_kms_drm_obj_destroy(sde_kms);
drm_obj_init_err:
sde_core_perf_destroy(&sde_kms->perf);
+hw_intr_init_err:
perf_err:
power_error:
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
index dcc0bd5..b77d64d 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
@@ -166,7 +166,7 @@
r = min((r1->x + r1->w), (r2->x + r2->w));
b = min((r1->y + r1->h), (r2->y + r2->h));
- if (r < l || b < t) {
+ if (r <= l || b <= t) {
memset(result, 0, sizeof(*result));
} else {
result->x = l;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index ad207d6..463c84e 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -585,6 +585,99 @@
sde_vbif_set_ot_limit(sde_kms, &ot_params);
}
+/**
+ * _sde_plane_set_vbif_qos - set vbif QoS for the given plane
+ * @plane: Pointer to drm plane
+ */
+static void _sde_plane_set_qos_remap(struct drm_plane *plane)
+{
+ struct sde_plane *psde;
+ struct sde_vbif_set_qos_params qos_params;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ if (!plane || !plane->dev) {
+ SDE_ERROR("invalid arguments\n");
+ return;
+ }
+
+ priv = plane->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+ psde = to_sde_plane(plane);
+ if (!psde->pipe_hw) {
+ SDE_ERROR("invalid pipe reference\n");
+ return;
+ }
+
+ memset(&qos_params, 0, sizeof(qos_params));
+ qos_params.vbif_idx = VBIF_RT;
+ qos_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+ qos_params.xin_id = psde->pipe_hw->cap->xin_id;
+ qos_params.num = psde->pipe_hw->idx - SSPP_VIG0;
+ qos_params.is_rt = psde->is_rt_pipe;
+
+ SDE_DEBUG("plane%d pipe:%d vbif:%d xin:%d rt:%d\n",
+ plane->base.id, qos_params.num,
+ qos_params.vbif_idx,
+ qos_params.xin_id, qos_params.is_rt);
+
+ sde_vbif_set_qos_remap(sde_kms, &qos_params);
+}
+
+/**
+ * _sde_plane_set_ts_prefill - set prefill with traffic shaper
+ * @plane: Pointer to drm plane
+ * @pstate: Pointer to sde plane state
+ */
+static void _sde_plane_set_ts_prefill(struct drm_plane *plane,
+ struct sde_plane_state *pstate)
+{
+ struct sde_plane *psde;
+ struct sde_hw_pipe_ts_cfg cfg;
+ struct msm_drm_private *priv;
+ struct sde_kms *sde_kms;
+
+ if (!plane || !plane->dev) {
+ SDE_ERROR("invalid arguments");
+ return;
+ }
+
+ priv = plane->dev->dev_private;
+ if (!priv || !priv->kms) {
+ SDE_ERROR("invalid KMS reference\n");
+ return;
+ }
+
+ sde_kms = to_sde_kms(priv->kms);
+ psde = to_sde_plane(plane);
+ if (!psde->pipe_hw) {
+ SDE_ERROR("invalid pipe reference\n");
+ return;
+ }
+
+ if (!psde->pipe_hw || !psde->pipe_hw->ops.setup_ts_prefill)
+ return;
+
+ _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_VBLANK_AMORTIZE);
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.size = sde_plane_get_property(pstate,
+ PLANE_PROP_PREFILL_SIZE);
+ cfg.time = sde_plane_get_property(pstate,
+ PLANE_PROP_PREFILL_TIME);
+
+ SDE_DEBUG("plane%d size:%llu time:%llu\n",
+ plane->base.id, cfg.size, cfg.time);
+ SDE_EVT32(DRMID(plane), cfg.size, cfg.time);
+ psde->pipe_hw->ops.setup_ts_prefill(psde->pipe_hw, &cfg,
+ pstate->multirect_index);
+}
+
/* helper to update a state's input fence pointer from the property */
static void _sde_plane_set_input_fence(struct sde_plane *psde,
struct sde_plane_state *pstate, uint64_t fd)
@@ -2041,6 +2134,23 @@
}
/**
+ * sde_plane_rot_flush - perform final flush related rotator options
+ * @plane: Pointer to drm plane
+ * @pstate: Pointer to sde plane state
+ */
+static void sde_plane_rot_flush(struct drm_plane *plane,
+ struct sde_plane_state *pstate)
+{
+ if (!plane || !pstate || !pstate->rot.rot_hw ||
+ !pstate->rot.rot_hw->ops.commit)
+ return;
+
+ pstate->rot.rot_hw->ops.commit(pstate->rot.rot_hw,
+ &pstate->rot.rot_cmd,
+ SDE_HW_ROT_CMD_START);
+}
+
+/**
* sde_plane_rot_destroy_state - destroy state for rotator stage
* @plane: Pointer to drm plane
* @state: Pointer to state to be destroyed
@@ -2709,13 +2819,15 @@
void sde_plane_flush(struct drm_plane *plane)
{
struct sde_plane *psde;
+ struct sde_plane_state *pstate;
- if (!plane) {
+ if (!plane || !plane->state) {
SDE_ERROR("invalid plane\n");
return;
}
psde = to_sde_plane(plane);
+ pstate = to_sde_plane_state(plane->state);
/*
* These updates have to be done immediately before the plane flush
@@ -2736,7 +2848,10 @@
/* flag h/w flush complete */
if (plane->state)
- to_sde_plane_state(plane->state)->pending = false;
+ pstate->pending = false;
+
+ /* signal inline rotator start */
+ sde_plane_rot_flush(plane, pstate);
}
static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
@@ -2833,6 +2948,10 @@
case PLANE_PROP_BLEND_OP:
/* no special action required */
break;
+ case PLANE_PROP_PREFILL_SIZE:
+ case PLANE_PROP_PREFILL_TIME:
+ pstate->dirty |= SDE_PLANE_DIRTY_PERF;
+ break;
case PLANE_PROP_ROT_DST_X:
case PLANE_PROP_ROT_DST_Y:
case PLANE_PROP_ROT_DST_W:
@@ -2988,7 +3107,7 @@
SDE_PIPE_SC_OP_MODE_OFFLINE;
pstate->sc_cfg.rd_en = false;
pstate->sc_cfg.rd_scid = 0;
- pstate->sc_cfg.rd_noallocate = false;
+ pstate->sc_cfg.rd_noallocate = true;
pstate->sc_cfg.rd_op_type =
SDE_PIPE_SC_RD_OP_TYPE_CACHEABLE;
}
@@ -3024,8 +3143,12 @@
if (plane->type != DRM_PLANE_TYPE_CURSOR) {
_sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL);
_sde_plane_set_ot_limit(plane, crtc);
+ if (pstate->dirty & SDE_PLANE_DIRTY_PERF)
+ _sde_plane_set_ts_prefill(plane, pstate);
}
+ _sde_plane_set_qos_remap(plane);
+
/* clear dirty */
pstate->dirty = 0x0;
@@ -3210,6 +3333,13 @@
msm_property_install_range(&psde->property_info, "color_fill",
0, 0, 0xFFFFFFFF, 0, PLANE_PROP_COLOR_FILL);
+ msm_property_install_range(&psde->property_info,
+ "prefill_size", 0x0, 0, ~0, 0,
+ PLANE_PROP_PREFILL_SIZE);
+ msm_property_install_range(&psde->property_info,
+ "prefill_time", 0x0, 0, ~0, 0,
+ PLANE_PROP_PREFILL_TIME);
+
info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL);
if (!info) {
SDE_ERROR("failed to allocate info memory\n");
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index 2056a70..47611d1 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -95,6 +95,7 @@
#define SDE_PLANE_DIRTY_RECTS 0x1
#define SDE_PLANE_DIRTY_FORMAT 0x2
#define SDE_PLANE_DIRTY_SHARPEN 0x4
+#define SDE_PLANE_DIRTY_PERF 0x8
#define SDE_PLANE_DIRTY_ALL 0xFFFFFFFF
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index c0c8248..c675216 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -210,6 +210,61 @@
return;
}
+void sde_vbif_set_qos_remap(struct sde_kms *sde_kms,
+ struct sde_vbif_set_qos_params *params)
+{
+ struct sde_hw_vbif *vbif = NULL;
+ struct sde_hw_mdp *mdp;
+ bool forced_on = false;
+ const struct sde_vbif_qos_tbl *qos_tbl;
+ int i;
+
+ if (!sde_kms || !params || !sde_kms->hw_mdp) {
+ SDE_ERROR("invalid arguments\n");
+ return;
+ }
+ mdp = sde_kms->hw_mdp;
+
+ for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
+ if (sde_kms->hw_vbif[i] &&
+ sde_kms->hw_vbif[i]->idx == params->vbif_idx) {
+ vbif = sde_kms->hw_vbif[i];
+ break;
+ }
+ }
+
+ if (!vbif || !vbif->cap) {
+ SDE_ERROR("invalid vbif %d\n", params->vbif_idx);
+ return;
+ }
+
+ if (!vbif->ops.set_qos_remap || !mdp->ops.setup_clk_force_ctrl) {
+ SDE_DEBUG("qos remap not supported\n");
+ return;
+ }
+
+ qos_tbl = params->is_rt ? &vbif->cap->qos_rt_tbl :
+ &vbif->cap->qos_nrt_tbl;
+
+ if (!qos_tbl->npriority_lvl || !qos_tbl->priority_lvl) {
+ SDE_DEBUG("qos tbl not defined\n");
+ return;
+ }
+
+ forced_on = mdp->ops.setup_clk_force_ctrl(mdp, params->clk_ctrl, true);
+
+ for (i = 0; i < qos_tbl->npriority_lvl; i++) {
+ SDE_DEBUG("vbif:%d xin:%d lvl:%d/%d\n",
+ params->vbif_idx, params->xin_id, i,
+ qos_tbl->priority_lvl[i]);
+ vbif->ops.set_qos_remap(vbif, params->xin_id, i,
+ qos_tbl->priority_lvl[i]);
+ }
+
+ if (forced_on)
+ mdp->ops.setup_clk_force_ctrl(mdp, params->clk_ctrl, false);
+}
+
#ifdef CONFIG_DEBUG_FS
void sde_debugfs_vbif_destroy(struct sde_kms *sde_kms)
{
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.h b/drivers/gpu/drm/msm/sde/sde_vbif.h
index 4b1cb1c..d05c2e0 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -28,6 +28,22 @@
};
/**
+ * struct sde_vbif_set_qos_params - QoS remapper parameter
+ * @vbif_idx: vbif identifier
+ * @xin_id: client interface identifier
+ * @clk_ctrl: clock control identifier of the xin
+ * @num: pipe identifier (debug only)
+ * @is_rt: true if pipe is used in real-time use case
+ */
+struct sde_vbif_set_qos_params {
+ u32 vbif_idx;
+ u32 xin_id;
+ u32 clk_ctrl;
+ u32 num;
+ bool is_rt;
+};
+
+/**
* sde_vbif_set_ot_limit - set OT limit for vbif client
* @sde_kms: SDE handler
* @params: Pointer to OT configuration parameters
@@ -35,6 +51,14 @@
void sde_vbif_set_ot_limit(struct sde_kms *sde_kms,
struct sde_vbif_set_ot_params *params);
+/**
+ * sde_vbif_set_qos_remap - set QoS priority level remap
+ * @sde_kms: SDE handler
+ * @params: Pointer to QoS configuration parameters
+ */
+void sde_vbif_set_qos_remap(struct sde_kms *sde_kms,
+ struct sde_vbif_set_qos_params *params);
+
#ifdef CONFIG_DEBUG_FS
int sde_debugfs_vbif_init(struct sde_kms *sde_kms, struct dentry *debugfs_root);
void sde_debugfs_vbif_destroy(struct sde_kms *sde_kms);
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
new file mode 100644
index 0000000..12165e8
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -0,0 +1,511 @@
+/* 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.
+ */
+
+#include <drm/drm_edid.h>
+
+#include "sde_kms.h"
+#include "sde_edid_parser.h"
+
+#define DBC_START_OFFSET 4
+#define EDID_DTD_LEN 18
+
+enum data_block_types {
+ RESERVED,
+ AUDIO_DATA_BLOCK,
+ VIDEO_DATA_BLOCK,
+ VENDOR_SPECIFIC_DATA_BLOCK,
+ SPEAKER_ALLOCATION_DATA_BLOCK,
+ VESA_DTC_DATA_BLOCK,
+ RESERVED2,
+ USE_EXTENDED_TAG
+};
+
+static u8 *sde_find_edid_extension(struct edid *edid, int ext_id)
+{
+ u8 *edid_ext = NULL;
+ int i;
+
+ /* No EDID or EDID extensions */
+ if (edid == NULL || edid->extensions == 0)
+ return NULL;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == ext_id)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return NULL;
+
+ return edid_ext;
+}
+
+static u8 *sde_find_cea_extension(struct edid *edid)
+{
+ return sde_find_edid_extension(edid, SDE_CEA_EXT);
+}
+
+static int
+sde_cea_db_payload_len(const u8 *db)
+{
+ return db[0] & 0x1f;
+}
+
+static int
+sde_cea_db_tag(const u8 *db)
+{
+ return db[0] >> 5;
+}
+
+static int
+sde_cea_revision(const u8 *cea)
+{
+ return cea[1];
+}
+
+static int
+sde_cea_db_offsets(const u8 *cea, int *start, int *end)
+{
+ /* Data block offset in CEA extension block */
+ *start = 4;
+ *end = cea[2];
+ if (*end == 0)
+ *end = 127;
+ if (*end < 4 || *end > 127)
+ return -ERANGE;
+ return 0;
+}
+
+#define sde_for_each_cea_db(cea, i, start, end) \
+for ((i) = (start); \
+(i) < (end) && (i) + sde_cea_db_payload_len(&(cea)[(i)]) < (end); \
+(i) += sde_cea_db_payload_len(&(cea)[(i)]) + 1)
+
+static u8 *sde_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((sde_cea_db_tag(db) == SDE_EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+static u8 *
+sde_edid_find_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if (sde_cea_db_tag(db) == blk_id)
+ return db;
+ }
+ }
+ return NULL;
+}
+
+
+static const u8 *_sde_edid_find_block(const u8 *in_buf, u32 start_offset,
+ u8 type, u8 *len)
+{
+ /* the start of data block collection, start of Video Data Block */
+ u32 offset = start_offset;
+ u32 dbc_offset = in_buf[2];
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ /*
+ * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
+ * collection present.
+ * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
+ * collection present and no DTD data present.
+ */
+ if ((dbc_offset == 0) || (dbc_offset == 4)) {
+ SDE_ERROR("EDID: no DTD or non-DTD data present\n");
+ return NULL;
+ }
+
+ while (offset < dbc_offset) {
+ u8 block_len = in_buf[offset] & 0x1F;
+
+ if ((offset + block_len <= dbc_offset) &&
+ (in_buf[offset] >> 5) == type) {
+ *len = block_len;
+ SDE_EDID_DEBUG("block=%d found @ 0x%x w/ len=%d\n",
+ type, offset, block_len);
+
+ return in_buf + offset;
+ }
+ offset += 1 + block_len;
+ }
+
+ return NULL;
+}
+
+static void sde_edid_extract_vendor_id(struct sde_edid_ctrl *edid_ctrl)
+{
+ char *vendor_id;
+ u32 id_codes;
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ vendor_id = edid_ctrl->vendor_id;
+ id_codes = ((u32)edid_ctrl->edid->mfg_id[0] << 8) +
+ edid_ctrl->edid->mfg_id[1];
+
+ vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F);
+ vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F);
+ vendor_id[2] = 'A' - 1 + (id_codes & 0x1F);
+ vendor_id[3] = 0;
+ SDE_EDID_DEBUG("vendor id is %s ", vendor_id);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_y420_support(struct drm_connector *connector,
+u32 video_format)
+{
+ u8 cea_mode = 0;
+ struct drm_display_mode *mode;
+
+ /* Need to add Y420 support flag to the modes */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ cea_mode = drm_match_cea_mode(mode);
+ if ((cea_mode != 0) && (cea_mode == video_format)) {
+ SDE_EDID_DEBUG("%s found match for %d ", __func__,
+ video_format);
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_YUV;
+ }
+ }
+}
+
+static void sde_edid_parse_Y420CMDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u32 offset = 0;
+ u8 len = 0;
+ u8 svd_len = 0;
+ const u8 *svd = NULL;
+ u32 i = 0, j = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: edid_ctrl is NULL\n", __func__);
+ return;
+ }
+
+ if (!db) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ len = db[0] & 0x1f;
+
+ if (len < 7)
+ return;
+ /* Byte 3 to L+1 contain SVDs */
+ offset += 2;
+
+ svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK);
+
+ if (svd) {
+ /*moving to the next byte as vic info begins there*/
+ ++svd;
+ svd_len = svd[0] & 0x1f;
+ }
+
+ for (i = 0; i < svd_len; i++, j++) {
+ video_format = *svd & 0x7F;
+ if (db[offset] & (1 << j))
+ sde_edid_set_y420_support(connector, video_format);
+
+ if (j & 0x80) {
+ j = j/8;
+ offset++;
+ if (offset >= len)
+ break;
+ }
+ }
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+
+}
+
+static void sde_edid_parse_Y420VDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u8 len = db[0] & 0x1f;
+ u32 i = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ /* Offset to byte 3 */
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ video_format = *(db + i) & 0x7F;
+ /*
+ * mode was already added in get_modes()
+ * only need to set the Y420 support flag
+ */
+ sde_edid_set_y420_support(connector, video_format);
+ }
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_mode_format(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl)
+{
+ const u8 *db = NULL;
+ struct drm_display_mode *mode;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ /* Set YUV mode support flags for YCbcr420VDB */
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_VIDEO_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420VDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 VDB is not present\n");
+
+ /* Set RGB supported on all modes where YUV is not set */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ if (!(mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV))
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_RGB;
+ }
+
+
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_CAPABILITY_MAP_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420CMDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 CMDB is not present\n");
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+}
+
+static void _sde_edid_extract_audio_data_blocks(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len = 0;
+ u8 adb_max = 0;
+ const u8 *adb = NULL;
+ u32 offset = DBC_START_OFFSET;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ edid_ctrl->adb_size = 0;
+
+ memset(edid_ctrl->audio_data_block, 0,
+ sizeof(edid_ctrl->audio_data_block));
+
+ do {
+ len = 0;
+ adb = _sde_edid_find_block(cea, offset, AUDIO_DATA_BLOCK,
+ &len);
+
+ if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE ||
+ adb_max >= MAX_NUMBER_ADB)) {
+ if (!edid_ctrl->adb_size) {
+ SDE_DEBUG("No/Invalid Audio Data Block\n");
+ return;
+ }
+
+ continue;
+ }
+
+ memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size,
+ adb + 1, len);
+ offset = (adb - cea) + 1 + len;
+
+ edid_ctrl->adb_size += len;
+ adb_max++;
+ } while (adb);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void _sde_edid_extract_speaker_allocation_data(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len;
+ const u8 *sadb = NULL;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ sadb = _sde_edid_find_block(cea, DBC_START_OFFSET,
+ SPEAKER_ALLOCATION_DATA_BLOCK, &len);
+ if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
+ SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n");
+ return;
+ }
+
+ memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
+ edid_ctrl->sadb_size = len;
+
+ SDE_EDID_DEBUG("speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
+ sadb[1],
+ (sadb[1] & BIT(0)) ? "FL/FR," : "",
+ (sadb[1] & BIT(1)) ? "LFE," : "",
+ (sadb[1] & BIT(2)) ? "FC," : "",
+ (sadb[1] & BIT(3)) ? "RL/RR," : "",
+ (sadb[1] & BIT(4)) ? "RC," : "",
+ (sadb[1] & BIT(5)) ? "FLC/FRC," : "",
+ (sadb[1] & BIT(6)) ? "RLC/RRC," : "");
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+struct sde_edid_ctrl *sde_edid_init(void)
+{
+ struct sde_edid_ctrl *edid_ctrl = NULL;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ edid_ctrl = kzalloc(sizeof(*edid_ctrl), GFP_KERNEL);
+ if (!edid_ctrl) {
+ SDE_ERROR("edid_ctrl alloc failed\n");
+ return NULL;
+ }
+ memset((edid_ctrl), 0, sizeof(*edid_ctrl));
+ SDE_EDID_DEBUG("%s -\n", __func__);
+ return edid_ctrl;
+}
+
+void sde_free_edid(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ kfree(edid_ctrl->edid);
+ edid_ctrl->edid = NULL;
+}
+
+void sde_edid_deinit(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ sde_free_edid((void *)&edid_ctrl);
+ kfree(edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *input)
+{
+ int rc = 0;
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (edid_ctrl->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ edid_ctrl->edid);
+
+ rc = drm_add_edid_modes(connector, edid_ctrl->edid);
+ sde_edid_set_mode_format(connector, edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+ return rc;
+ }
+
+ drm_mode_connector_update_edid_property(connector, NULL);
+ SDE_EDID_DEBUG("%s null edid -", __func__);
+ return rc;
+}
+
+bool sde_detect_hdmi_monitor(void *input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ return drm_detect_hdmi_monitor(edid_ctrl->edid);
+}
+
+void sde_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter, void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ edid_ctrl->edid = drm_get_edid(connector, adapter);
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ if (!edid_ctrl->edid)
+ SDE_ERROR("EDID read failed\n");
+
+ if (edid_ctrl->edid) {
+ sde_edid_extract_vendor_id(edid_ctrl);
+ _sde_edid_extract_audio_data_blocks(edid_ctrl);
+ _sde_edid_extract_speaker_allocation_data(edid_ctrl);
+ }
+ SDE_EDID_DEBUG("%s -\n", __func__);
+};
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h
new file mode 100644
index 0000000..1143dc2
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.h
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef _SDE_EDID_PARSER_H_
+#define _SDE_EDID_PARSER_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+
+#define MAX_NUMBER_ADB 5
+#define MAX_AUDIO_DATA_BLOCK_SIZE 30
+#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+#define EDID_VENDOR_ID_SIZE 4
+
+#define SDE_CEA_EXT 0x02
+#define SDE_EXTENDED_TAG 0x07
+
+enum extended_data_block_types {
+ VIDEO_CAPABILITY_DATA_BLOCK = 0x0,
+ VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01,
+ HDMI_VIDEO_DATA_BLOCK = 0x04,
+ HDR_STATIC_METADATA_DATA_BLOCK = 0x06,
+ Y420_VIDEO_DATA_BLOCK = 0x0E,
+ VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D,
+ Y420_CAPABILITY_MAP_DATA_BLOCK = 0x0F,
+ VENDOR_SPECIFIC_AUDIO_DATA_BLOCK = 0x11,
+ INFOFRAME_DATA_BLOCK = 0x20,
+};
+
+#ifdef SDE_EDID_DEBUG_ENABLE
+#define SDE_EDID_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_EDID_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+/*
+ * struct hdmi_edid_hdr_data - HDR Static Metadata
+ * @eotf: Electro-Optical Transfer Function
+ * @metadata_type_one: Static Metadata Type 1 support
+ * @max_luminance: Desired Content Maximum Luminance
+ * @avg_luminance: Desired Content Frame-average Luminance
+ * @min_luminance: Desired Content Minimum Luminance
+ */
+struct sde_edid_hdr_data {
+ u32 eotf;
+ bool metadata_type_one;
+ u32 max_luminance;
+ u32 avg_luminance;
+ u32 min_luminance;
+};
+
+struct sde_edid_sink_caps {
+ u32 max_pclk_in_hz;
+ bool scdc_present;
+ bool scramble_support; /* scramble support for less than 340Mcsc */
+ bool read_req_support;
+ bool osd_disparity;
+ bool dual_view_support;
+ bool ind_view_support;
+};
+
+struct sde_edid_ctrl {
+ struct edid *edid;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
+ int adb_size;
+ u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
+ int sadb_size;
+ bool hdr_supported;
+ char vendor_id[EDID_VENDOR_ID_SIZE];
+ struct sde_edid_sink_caps sink_caps;
+ struct sde_edid_hdr_data hdr_data;
+};
+
+/**
+ * sde_edid_init() - init edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ * Return: handle to sde_edid_ctrl for the client.
+ */
+struct sde_edid_ctrl *sde_edid_init(void);
+
+/**
+ * sde_edid_deinit() - deinit edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_edid_deinit(void **edid_ctrl);
+
+/**
+ * sde_get_edid() - get edid info.
+ * @connector: Handle to the drm_connector.
+ * @adapter: handle to i2c adapter for DDC read
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_get_edid(struct drm_connector *connector,
+struct i2c_adapter *adapter,
+void **edid_ctrl);
+
+/**
+ * sde_free_edid() - free edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_free_edid(void **edid_ctrl);
+
+/**
+ * sde_detect_hdmi_monitor() - detect HDMI mode.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+bool sde_detect_hdmi_monitor(void *edid_ctrl);
+
+/**
+ * _sde_edid_update_modes() - populate EDID modes.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *edid_ctrl);
+
+#endif /* _SDE_EDID_PARSER_H_ */
+
diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c
index 1e4f6b1..fb7f85c 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.c
+++ b/drivers/gpu/drm/msm/sde_power_handle.c
@@ -333,6 +333,31 @@
return -EINVAL;
}
+ pdbus->ab_rt = ab_quota_rt;
+ pdbus->ib_rt = ib_quota_rt;
+ pdbus->ab_nrt = ab_quota_nrt;
+ pdbus->ib_nrt = ib_quota_nrt;
+
+ if (pdbus->enable) {
+ ab_quota_rt = max_t(u64, ab_quota_rt,
+ SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA);
+ ib_quota_rt = max_t(u64, ib_quota_rt,
+ SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+ ab_quota_nrt = max_t(u64, ab_quota_nrt,
+ SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA);
+ ib_quota_nrt = max_t(u64, ib_quota_nrt,
+ SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+ } else {
+ ab_quota_rt = max_t(u64, ab_quota_rt,
+ SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA);
+ ib_quota_rt = max_t(u64, ib_quota_rt,
+ SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA);
+ ab_quota_nrt = max_t(u64, ab_quota_nrt,
+ SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA);
+ ib_quota_nrt = max_t(u64, ib_quota_nrt,
+ SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA);
+ }
+
if (!ab_quota_rt && !ab_quota_nrt && !ib_quota_rt && !ib_quota_nrt) {
new_uc_idx = 0;
} else {
@@ -571,19 +596,12 @@
bool enable)
{
int rc = 0;
- u64 ab_quota_rt, ab_quota_nrt;
- u64 ib_quota_rt, ib_quota_nrt;
- ab_quota_rt = ab_quota_nrt = enable ?
- SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA :
- SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA;
- ib_quota_rt = ib_quota_nrt = enable ?
- SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA :
- SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA;
+ pdbus->enable = enable;
if (pdbus->data_bus_hdl)
- rc = _sde_power_data_bus_set_quota(pdbus, ab_quota_rt,
- ab_quota_nrt, ib_quota_rt, ib_quota_nrt);
+ rc = _sde_power_data_bus_set_quota(pdbus, pdbus->ab_rt,
+ pdbus->ab_nrt, pdbus->ib_rt, pdbus->ib_nrt);
if (rc)
pr_err("failed to set data bus vote rc=%d enable:%d\n",
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 38bf21f..c526b71 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -16,9 +16,9 @@
#define MAX_CLIENT_NAME_LEN 128
-#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA 6000000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA 2000000
#define SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA 0
-#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 6000000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA 2000000
#define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA 0
#include <linux/sde_io_util.h>
@@ -93,6 +93,11 @@
* @bus_channels: number of memory bus channels
* @curr_bw_uc_idx: current use case index of data bus
* @ao_bw_uc_idx: active only use case index of data bus
+ * @ab_rt: realtime ab quota
+ * @ib_rt: realtime ib quota
+ * @ab_nrt: non-realtime ab quota
+ * @ib_nrt: non-realtime ib quota
+ * @enable: true if bus is enabled
*/
struct sde_power_data_bus_handle {
struct msm_bus_scale_pdata *data_bus_scale_table;
@@ -102,6 +107,11 @@
u32 bus_channels;
u32 curr_bw_uc_idx;
u32 ao_bw_uc_idx;
+ u64 ab_rt;
+ u64 ib_rt;
+ u64 ab_nrt;
+ u64 ib_nrt;
+ bool enable;
};
/*
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index cab7e0f..7bf2211 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -123,6 +123,7 @@
void sde_rsc_client_destroy(struct sde_rsc_client *client)
{
struct sde_rsc_priv *rsc;
+ enum sde_rsc_state state;
if (!client) {
pr_debug("invalid client\n");
@@ -138,9 +139,13 @@
goto end;
mutex_lock(&rsc->client_lock);
- if (client->current_state != SDE_RSC_IDLE_STATE)
+ state = client->current_state;
+ mutex_unlock(&rsc->client_lock);
+
+ if (state != SDE_RSC_IDLE_STATE)
sde_rsc_client_state_update(client, SDE_RSC_IDLE_STATE,
NULL, -1);
+ mutex_lock(&rsc->client_lock);
list_del_init(&client->list);
mutex_unlock(&rsc->client_lock);
@@ -215,6 +220,39 @@
}
EXPORT_SYMBOL(sde_rsc_unregister_event);
+bool is_sde_rsc_available(int rsc_index)
+{
+ if (rsc_index >= MAX_RSC_COUNT) {
+ pr_err("invalid rsc index:%d\n", rsc_index);
+ return false;
+ } else if (!rsc_prv_list[rsc_index]) {
+ pr_err("rsc idx:%d not probed yet or not available\n",
+ rsc_index);
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(is_sde_rsc_available);
+
+enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
+{
+ struct sde_rsc_priv *rsc;
+
+ if (rsc_index >= MAX_RSC_COUNT) {
+ pr_err("invalid rsc index:%d\n", rsc_index);
+ return SDE_RSC_IDLE_STATE;
+ } else if (!rsc_prv_list[rsc_index]) {
+ pr_err("rsc idx:%d not probed yet or not available\n",
+ rsc_index);
+ return SDE_RSC_IDLE_STATE;
+ }
+
+ rsc = rsc_prv_list[rsc_index];
+ return rsc->current_state;
+}
+EXPORT_SYMBOL(get_sde_rsc_current_state);
+
static int sde_rsc_clk_enable(struct sde_power_handle *phandle,
struct sde_power_client *pclient, bool enable)
{
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
index 8894fee..cbacbb6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
@@ -130,7 +130,7 @@
poll = false;
}
- if (list_empty(&therm->alarm.head) && poll)
+ if (poll)
nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
spin_unlock_irqrestore(&therm->lock, flags);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
index 91198d7..e2fecce 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
@@ -83,7 +83,7 @@
spin_unlock_irqrestore(&fan->lock, flags);
/* schedule next fan update, if not at target speed already */
- if (list_empty(&fan->alarm.head) && target != duty) {
+ if (target != duty) {
u16 bump_period = fan->bios.bump_period;
u16 slow_down_period = fan->bios.slow_down_period;
u64 delay;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
index 59701b7..ff9fbe7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c
@@ -53,7 +53,7 @@
duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff);
nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty);
- if (list_empty(&fan->alarm.head) && percent != (duty * 100)) {
+ if (percent != (duty * 100)) {
u64 next_change = (percent * fan->period_us) / 100;
if (!duty)
next_change = fan->period_us - next_change;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
index b9703c0..9a79e91 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
@@ -185,7 +185,7 @@
spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
/* schedule the next poll in one second */
- if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head))
+ if (therm->func->temp_get(therm) >= 0)
nvkm_timer_alarm(tmr, 1000000000ULL, alarm);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
index 07dc82b..f2a86ea 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c
@@ -36,23 +36,29 @@
unsigned long flags;
LIST_HEAD(exec);
- /* move any due alarms off the pending list */
+ /* Process pending alarms. */
spin_lock_irqsave(&tmr->lock, flags);
list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) {
- if (alarm->timestamp <= nvkm_timer_read(tmr))
- list_move_tail(&alarm->head, &exec);
+ /* Have we hit the earliest alarm that hasn't gone off? */
+ if (alarm->timestamp > nvkm_timer_read(tmr)) {
+ /* Schedule it. If we didn't race, we're done. */
+ tmr->func->alarm_init(tmr, alarm->timestamp);
+ if (alarm->timestamp > nvkm_timer_read(tmr))
+ break;
+ }
+
+ /* Move to completed list. We'll drop the lock before
+ * executing the callback so it can reschedule itself.
+ */
+ list_move_tail(&alarm->head, &exec);
}
- /* reschedule interrupt for next alarm time */
- if (!list_empty(&tmr->alarms)) {
- alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head);
- tmr->func->alarm_init(tmr, alarm->timestamp);
- } else {
+ /* Shut down interrupt if no more pending alarms. */
+ if (list_empty(&tmr->alarms))
tmr->func->alarm_fini(tmr);
- }
spin_unlock_irqrestore(&tmr->lock, flags);
- /* execute any pending alarm handlers */
+ /* Execute completed callbacks. */
list_for_each_entry_safe(alarm, atemp, &exec, head) {
list_del_init(&alarm->head);
alarm->func(alarm);
@@ -65,24 +71,37 @@
struct nvkm_alarm *list;
unsigned long flags;
- alarm->timestamp = nvkm_timer_read(tmr) + nsec;
-
- /* append new alarm to list, in soonest-alarm-first order */
+ /* Remove alarm from pending list.
+ *
+ * This both protects against the corruption of the list,
+ * and implements alarm rescheduling/cancellation.
+ */
spin_lock_irqsave(&tmr->lock, flags);
- if (!nsec) {
- if (!list_empty(&alarm->head))
- list_del(&alarm->head);
- } else {
+ list_del_init(&alarm->head);
+
+ if (nsec) {
+ /* Insert into pending list, ordered earliest to latest. */
+ alarm->timestamp = nvkm_timer_read(tmr) + nsec;
list_for_each_entry(list, &tmr->alarms, head) {
if (list->timestamp > alarm->timestamp)
break;
}
+
list_add_tail(&alarm->head, &list->head);
+
+ /* Update HW if this is now the earliest alarm. */
+ list = list_first_entry(&tmr->alarms, typeof(*list), head);
+ if (list == alarm) {
+ tmr->func->alarm_init(tmr, alarm->timestamp);
+ /* This shouldn't happen if callers aren't stupid.
+ *
+ * Worst case scenario is that it'll take roughly
+ * 4 seconds for the next alarm to trigger.
+ */
+ WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr));
+ }
}
spin_unlock_irqrestore(&tmr->lock, flags);
-
- /* process pending alarms */
- nvkm_timer_alarm_trigger(tmr);
}
void
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
index 7b9ce87..7f48249 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c
@@ -76,8 +76,8 @@
u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0);
if (stat & 0x00000001) {
- nvkm_timer_alarm_trigger(tmr);
nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001);
+ nvkm_timer_alarm_trigger(tmr);
stat &= ~0x00000001;
}
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index e5cfd69..1f76233a 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -114,6 +114,7 @@
#define A6XX_RBBM_INT_0_STATUS 0x201
#define A6XX_RBBM_STATUS 0x210
#define A6XX_RBBM_STATUS3 0x213
+#define A6XX_RBBM_VBIF_GX_RESET_STATUS 0x215
#define A6XX_RBBM_PERFCTR_CP_0_LO 0x400
#define A6XX_RBBM_PERFCTR_CP_0_HI 0x401
#define A6XX_RBBM_PERFCTR_CP_1_LO 0x402
@@ -782,6 +783,7 @@
#define A6XX_GMU_GX_SPTPRAC_POWER_CONTROL 0x1A881
#define A6XX_GMU_CM3_ITCM_START 0x1B400
#define A6XX_GMU_CM3_DTCM_START 0x1C400
+#define A6XX_GMU_NMI_CONTROL_STATUS 0x1CBF0
#define A6XX_GMU_BOOT_SLUMBER_OPTION 0x1CBF8
#define A6XX_GMU_GX_VOTE_IDX 0x1CBF9
#define A6XX_GMU_MX_VOTE_IDX 0x1CBFA
@@ -793,6 +795,7 @@
#define A6XX_GMU_CM3_BOOT_CONFIG 0x1F801
#define A6XX_GMU_CM3_FW_BUSY 0x1F81A
#define A6XX_GMU_CM3_FW_INIT_RESULT 0x1F81C
+#define A6XX_GMU_CM3_CFG 0x1F82D
#define A6XX_GMU_PWR_COL_INTER_FRAME_CTRL 0x1F8C0
#define A6XX_GMU_PWR_COL_INTER_FRAME_HYST 0x1F8C1
#define A6XX_GMU_PWR_COL_SPTPRAC_HYST 0x1F8C2
@@ -802,6 +805,8 @@
#define A6XX_GMU_RPMH_HYST_CTRL 0x1F8E9
#define A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE 0x1F8EC
#define A6XX_GMU_BOOT_KMD_LM_HANDSHAKE 0x1F9F0
+#define A6XX_GMU_LLM_GLM_SLEEP_CTRL 0x1F957
+#define A6XX_GMU_LLM_GLM_SLEEP_STATUS 0x1F958
/* HFI registers*/
#define A6XX_GMU_ALWAYS_ON_COUNTER_L 0x1F888
@@ -831,6 +836,10 @@
#define A6XX_GMU_HOST2GMU_INTR_INFO_3 0x1F99E
#define A6XX_GMU_GENERAL_7 0x1F9CC
+/* ISENSE registers */
+#define A6XX_GMU_ISENSE_CTRL 0x1F95D
+#define A6XX_GPU_CS_ENABLE_REG 0x23120
+
#define A6XX_GMU_AO_INTERRUPT_EN 0x23B03
#define A6XX_GMU_AO_HOST_INTERRUPT_CLR 0x23B04
#define A6XX_GMU_AO_HOST_INTERRUPT_STATUS 0x23B05
@@ -851,6 +860,9 @@
#define A6XX_GMU_AHB_FENCE_RANGE_0 0x23B11
#define A6XX_GMU_AHB_FENCE_RANGE_1 0x23B12
+/* GPUCC registers */
+#define A6XX_GPU_CC_GX_GDSCR 0x24403
+
/* GPU RSC sequencer registers */
#define A6XX_RSCC_PDC_SEQ_START_ADDR 0x23408
#define A6XX_RSCC_PDC_MATCH_VALUE_LO 0x23409
@@ -865,6 +877,10 @@
#define A6XX_RSCC_OVERRIDE_START_ADDR 0x23500
#define A6XX_RSCC_SEQ_BUSY_DRV0 0x23501
#define A6XX_RSCC_SEQ_MEM_0_DRV0 0x23580
+#define A6XX_RSCC_TCS0_DRV0_STATUS 0x23746
+#define A6XX_RSCC_TCS1_DRV0_STATUS 0x238AE
+#define A6XX_RSCC_TCS2_DRV0_STATUS 0x23A16
+#define A6XX_RSCC_TCS3_DRV0_STATUS 0x23B7E
/* GPU PDC sequencer registers in AOSS.RPMh domain */
#define PDC_GPU_ENABLE_PDC 0x21140
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 2e92335..627b351 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -56,9 +56,6 @@
#define DRIVER_VERSION_MAJOR 3
#define DRIVER_VERSION_MINOR 1
-/* Number of times to try hard reset */
-#define NUM_TIMES_RESET_RETRY 5
-
#define KGSL_LOG_LEVEL_DEFAULT 3
static void adreno_input_work(struct work_struct *work);
@@ -514,8 +511,6 @@
.id_table = adreno_input_ids,
};
-static int adreno_soft_reset(struct kgsl_device *device);
-
/*
* _soft_reset() - Soft reset GPU
* @adreno_dev: Pointer to adreno device
@@ -526,7 +521,7 @@
* all the HW logic, restores GPU registers to default state and
* flushes out pending VBIF transactions.
*/
-static void _soft_reset(struct adreno_device *adreno_dev)
+static int _soft_reset(struct adreno_device *adreno_dev)
{
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
unsigned int reg;
@@ -555,6 +550,8 @@
if (gpudev->regulator_enable)
gpudev->regulator_enable(adreno_dev);
+
+ return 0;
}
@@ -1623,7 +1620,7 @@
* Power up the GPU and initialize it. If priority is specified then elevate
* the thread priority for the duration of the start operation
*/
-static int adreno_start(struct kgsl_device *device, int priority)
+int adreno_start(struct kgsl_device *device, int priority)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int nice = task_nice(current);
@@ -1640,38 +1637,6 @@
return ret;
}
-/**
- * adreno_vbif_clear_pending_transactions() - Clear transactions in VBIF pipe
- * @device: Pointer to the device whose VBIF pipe is to be cleared
- */
-static int adreno_vbif_clear_pending_transactions(struct kgsl_device *device)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- unsigned int mask = gpudev->vbif_xin_halt_ctrl0_mask;
- unsigned int val;
- unsigned long wait_for_vbif;
- int ret = 0;
-
- adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, mask);
- /* wait for the transactions to clear */
- wait_for_vbif = jiffies + msecs_to_jiffies(100);
- while (1) {
- adreno_readreg(adreno_dev,
- ADRENO_REG_VBIF_XIN_HALT_CTRL1, &val);
- if ((val & mask) == mask)
- break;
- if (time_after(jiffies, wait_for_vbif)) {
- KGSL_DRV_ERR(device,
- "Wait limit reached for VBIF XIN Halt\n");
- ret = -ETIMEDOUT;
- break;
- }
- }
- adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, 0);
- return ret;
-}
-
static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
{
int i;
@@ -2340,12 +2305,20 @@
* The GPU hardware is reset but we never pull power so we can skip
* a lot of the standard adreno_stop/adreno_start sequence
*/
-static int adreno_soft_reset(struct kgsl_device *device)
+int adreno_soft_reset(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
int ret;
+ if (gpudev->oob_set) {
+ ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
+ OOB_CPINIT_CHECK_MASK,
+ OOB_CPINIT_CLEAR_MASK);
+ if (ret)
+ return ret;
+ }
+
kgsl_pwrctrl_change_state(device, KGSL_STATE_AWARE);
adreno_set_active_ctxs_null(adreno_dev);
@@ -2359,7 +2332,15 @@
adreno_perfcounter_save(adreno_dev);
/* Reset the GPU */
- _soft_reset(adreno_dev);
+ if (gpudev->soft_reset)
+ ret = gpudev->soft_reset(adreno_dev);
+ else
+ ret = _soft_reset(adreno_dev);
+ if (ret) {
+ if (gpudev->oob_clear)
+ gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+ return ret;
+ }
/* Set the page table back to the default page table */
adreno_ringbuffer_set_global(adreno_dev, 0);
@@ -2401,6 +2382,9 @@
/* Restore physical performance counter values after soft reset */
adreno_perfcounter_restore(adreno_dev);
+ if (gpudev->oob_clear)
+ gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+
return ret;
}
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 78cecd0..91f03d0 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -159,10 +159,12 @@
#define KGSL_END_OF_PROFILE_IDENTIFIER 0x2DEFADE2
#define KGSL_PWRON_FIXUP_IDENTIFIER 0x2AFAFAFA
+/* Number of times to try hard reset */
+#define NUM_TIMES_RESET_RETRY 5
+
/* One cannot wait forever for the core to idle, so set an upper limit to the
* amount of time to wait for the core to go idle
*/
-
#define ADRENO_IDLE_TIMEOUT (20 * 1000)
#define ADRENO_UCHE_GMEM_BASE 0x100000
@@ -204,6 +206,7 @@
#define ADRENO_TIMEOUT_FAULT BIT(2)
#define ADRENO_IOMMU_PAGE_FAULT BIT(3)
#define ADRENO_PREEMPT_FAULT BIT(4)
+#define ADRENO_GMU_FAULT BIT(5)
#define ADRENO_SPTP_PC_CTRL 0
#define ADRENO_PPD_CTRL 1
@@ -499,6 +502,7 @@
* attached and enabled
* @ADRENO_DEVICE_CACHE_FLUSH_TS_SUSPENDED - Set if a CACHE_FLUSH_TS irq storm
* is in progress
+ * @ADRENO_DEVICE_HARD_RESET - Set if soft reset fails and hard reset is needed
*/
enum adreno_device_flags {
ADRENO_DEVICE_PWRON = 0,
@@ -515,6 +519,7 @@
ADRENO_DEVICE_GPMU_INITIALIZED = 11,
ADRENO_DEVICE_ISDB_ENABLED = 12,
ADRENO_DEVICE_CACHE_FLUSH_TS_SUSPENDED = 13,
+ ADRENO_DEVICE_HARD_RESET = 14,
};
/**
@@ -864,6 +869,8 @@
int (*wait_for_gmu_idle)(struct adreno_device *);
const char *(*iommu_fault_block)(struct adreno_device *adreno_dev,
unsigned int fsynr1);
+ int (*reset)(struct kgsl_device *, int fault);
+ int (*soft_reset)(struct adreno_device *);
};
/**
@@ -952,6 +959,8 @@
extern int adreno_wake_nice;
extern unsigned int adreno_wake_timeout;
+int adreno_start(struct kgsl_device *device, int priority);
+int adreno_soft_reset(struct kgsl_device *device);
long adreno_ioctl(struct kgsl_device_private *dev_priv,
unsigned int cmd, unsigned long arg);
@@ -1707,4 +1716,37 @@
kgsl_active_count_put(KGSL_DEVICE(adreno_dev));
}
+/**
+ * adreno_vbif_clear_pending_transactions() - Clear transactions in VBIF pipe
+ * @device: Pointer to the device whose VBIF pipe is to be cleared
+ */
+static inline int adreno_vbif_clear_pending_transactions(
+ struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+ unsigned int mask = gpudev->vbif_xin_halt_ctrl0_mask;
+ unsigned int val;
+ unsigned long wait_for_vbif;
+ int ret = 0;
+
+ adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, mask);
+ /* wait for the transactions to clear */
+ wait_for_vbif = jiffies + msecs_to_jiffies(100);
+ while (1) {
+ adreno_readreg(adreno_dev,
+ ADRENO_REG_VBIF_XIN_HALT_CTRL1, &val);
+ if ((val & mask) == mask)
+ break;
+ if (time_after(jiffies, wait_for_vbif)) {
+ KGSL_DRV_ERR(device,
+ "Wait limit reached for VBIF XIN Halt\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+ adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, 0);
+ return ret;
+}
+
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index e157e7b..9a56bec 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -29,8 +29,6 @@
#include "kgsl_gmu.h"
#include "kgsl_trace.h"
-#define OOB_REQUEST_TIMEOUT 10 /* ms */
-
#define A6XX_CP_RB_CNTL_DEFAULT (((ilog2(4) << 8) & 0x1F00) | \
(ilog2(KGSL_RB_DWORDS >> 1) & 0x3F))
@@ -789,8 +787,10 @@
wmb();
}
-#define GMU_START_TIMEOUT 10 /* ms */
-#define GPU_START_TIMEOUT 100 /* ms */
+#define GMU_START_TIMEOUT 10 /* ms */
+#define GPU_START_TIMEOUT 100 /* ms */
+#define GPU_RESET_TIMEOUT 1 /* ms */
+#define GPU_RESET_TIMEOUT_US 10 /* us */
/*
* timed_poll_check() - polling *gmu* register at given offset until
@@ -962,7 +962,9 @@
GPU_START_TIMEOUT,
check_mask)) {
ret = -ETIMEDOUT;
- dev_err(&gmu->pdev->dev, "OOB set timed out\n");
+ dev_err(&gmu->pdev->dev,
+ "OOB set timed out, mask %x\n", set_mask);
+ WARN_ON(true);
}
kgsl_gmu_regwrite(device, A6XX_GMU_GMU2HOST_INTR_CLR, clear_mask);
@@ -1005,6 +1007,7 @@
#define SPTPRAC_POWEROFF_STATUS_MASK BIT(2)
#define SPTPRAC_POWERON_STATUS_MASK BIT(3)
#define SPTPRAC_CTRL_TIMEOUT 10 /* ms */
+#define A6XX_RETAIN_FF_ENABLE_ENABLE_MASK BIT(11)
/*
* a6xx_sptprac_enable() - Power on SPTPRAC
@@ -1045,6 +1048,10 @@
if (!gmu->pdev)
return;
+ /* Ensure that retention is on */
+ kgsl_gmu_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0,
+ A6XX_RETAIN_FF_ENABLE_ENABLE_MASK);
+
kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL,
SPTPRAC_POWEROFF_CTRL_MASK);
@@ -1073,7 +1080,7 @@
ret = regulator_enable(gmu->gx_gdsc);
if (ret) {
dev_err(&gmu->pdev->dev,
- "Failed to turn on GPU HM HS\n");
+ "Failed to turn on GPU HM HS\n");
return ret;
}
@@ -1099,6 +1106,10 @@
if (!regulator_is_enabled(gmu->gx_gdsc))
return 0;
+ /* Ensure that retention is on */
+ kgsl_gmu_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0,
+ A6XX_RETAIN_FF_ENABLE_ENABLE_MASK);
+
clk_disable_unprepare(pwr->grp_clks[0]);
clk_set_rate(pwr->grp_clks[0],
@@ -1119,11 +1130,14 @@
/* If GMU does not control HM we must */
if (gmu->idle_level < GPU_HW_IFPC) {
+
ret = a6xx_hm_enable(ADRENO_DEVICE(device));
if (ret) {
dev_err(&gmu->pdev->dev, "Failed to power on GPU HM\n");
return ret;
}
+
+
}
/* If GMU does not control SPTPRAC we must */
@@ -1162,19 +1176,6 @@
}
/*
- * a6xx_hm_sptprac_control() - Turn HM and SPTPRAC on or off
- * @device: Pointer to KGSL device
- * @on: True to turn on or false to turn off
- */
-static int a6xx_hm_sptprac_control(struct kgsl_device *device, bool on)
-{
- if (on)
- return a6xx_hm_sptprac_enable(device);
- else
- return a6xx_hm_sptprac_disable(device);
-}
-
-/*
* a6xx_gfx_rail_on() - request GMU to power GPU at given OPP.
* @device: Pointer to KGSL device
*
@@ -1206,6 +1207,8 @@
return ret;
}
+#define GMU_POWER_STATE_SLUMBER 15
+
/*
* a6xx_notify_slumber() - initiate request to GMU to prepare to slumber
* @device: Pointer to KGSL device
@@ -1281,13 +1284,12 @@
kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0);
/* Turn on the HM and SPTP head switches */
- ret = a6xx_hm_sptprac_control(device, true);
+ ret = a6xx_hm_sptprac_enable(device);
return ret;
-
error_rsc:
dev_err(dev, "GPU RSC sequence stuck in waking up GPU\n");
- return -EINVAL;
+ return -EINVAL;
}
static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device)
@@ -1296,7 +1298,7 @@
int val, ret = 0;
/* Turn off the SPTP and HM head switches */
- ret = a6xx_hm_sptprac_control(device, false);
+ ret = a6xx_hm_sptprac_disable(device);
/* RSC sleep sequence */
kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
@@ -1339,7 +1341,12 @@
struct gmu_memdesc *mem_addr = gmu->hfi_mem;
int ret, i;
- if (boot_state == GMU_COLD_BOOT || boot_state == GMU_RESET) {
+ switch (boot_state) {
+ case GMU_COLD_BOOT:
+ /* Turn on the HM and SPTP head switches */
+ ret = a6xx_hm_sptprac_enable(device);
+ if (ret)
+ return ret;
/* Turn on TCM retention */
kgsl_gmu_regwrite(device, A6XX_GMU_GENERAL_7, 1);
@@ -1347,7 +1354,7 @@
if (!test_and_set_bit(GMU_BOOT_INIT_DONE, &gmu->flags)) {
_load_gmu_rpmh_ucode(device);
/* Turn on the HM and SPTP head switches */
- ret = a6xx_hm_sptprac_control(device, true);
+ ret = a6xx_hm_sptprac_enable(device);
if (ret)
return ret;
} else {
@@ -1371,10 +1378,19 @@
gmu->load_mode);
return -EINVAL;
}
- } else {
+ break;
+ case GMU_WARM_BOOT:
ret = a6xx_rpmh_power_on_gpu(device);
if (ret)
return ret;
+ break;
+ case GMU_RESET:
+ /* Turn on the HM and SPTP head switches */
+ ret = a6xx_hm_sptprac_enable(device);
+ if (ret)
+ return ret;
+ default:
+ break;
}
/* Clear init result to make sure we are getting fresh value */
@@ -1394,8 +1410,7 @@
if (ret)
return ret;
- if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)
- && boot_state == GMU_COLD_BOOT) {
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) {
ret = a6xx_gfx_rail_on(device);
if (ret) {
a6xx_oob_clear(adreno_dev,
@@ -1425,7 +1440,7 @@
unsigned int perf_idx, unsigned int bw_idx)
{
struct hfi_dcvs_cmd dcvs_cmd = {
- .ack_type = ACK_BLOCK,
+ .ack_type = ACK_NONBLOCK,
.freq = {
.perf_idx = perf_idx,
.clkset_opt = OPTION_AT_LEAST,
@@ -1439,10 +1454,6 @@
union gpu_perf_vote vote;
int ret;
- if (device->state == KGSL_STATE_INIT ||
- device->state == KGSL_STATE_SUSPEND)
- dcvs_cmd.ack_type = ACK_NONBLOCK;
-
kgsl_gmu_regwrite(device, A6XX_GMU_DCVS_ACK_OPTION, dcvs_cmd.ack_type);
vote.fvote = dcvs_cmd.freq;
@@ -1469,43 +1480,6 @@
return ret;
}
-/*
- * a6xx_rpmh_gpu_pwrctrl() - GPU power control via RPMh/GMU interface
- * @adreno_dev: Pointer to adreno device
- * @mode: requested power mode
- * @arg1: first argument for mode control
- * @arg2: second argument for mode control
- */
-static int a6xx_rpmh_gpu_pwrctrl(struct adreno_device *adreno_dev,
- unsigned int mode, unsigned int arg1, unsigned int arg2)
-{
- struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- struct gmu_device *gmu = &device->gmu;
- int ret;
-
- switch (mode) {
- case GMU_FW_START:
- ret = a6xx_gmu_fw_start(device, arg1);
- break;
- case GMU_FW_STOP:
- ret = a6xx_rpmh_power_off_gpu(device);
- break;
- case GMU_DCVS_NOHFI:
- ret = a6xx_gmu_dcvs_nohfi(device, arg1, arg2);
- break;
- case GMU_NOTIFY_SLUMBER:
- ret = a6xx_notify_slumber(device);
- break;
- default:
- dev_err(&gmu->pdev->dev,
- "unsupported GMU power ctrl mode:%d\n", mode);
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
static bool a6xx_hw_isidle(struct adreno_device *adreno_dev)
{
unsigned int reg;
@@ -1585,6 +1559,290 @@
ADRENO_FW(adreno_dev, ADRENO_FW_SQE));
}
+#define VBIF_RESET_ACK_TIMEOUT 100
+#define VBIF_RESET_ACK_MASK 0x00f0
+
+static int a6xx_soft_reset(struct adreno_device *adreno_dev)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ unsigned int reg;
+
+ /*
+ * For the soft reset case with GMU enabled this part is done
+ * by the GMU firmware
+ */
+ if (kgsl_gmu_isenabled(device) &&
+ !test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv))
+ return 0;
+
+
+ adreno_writereg(adreno_dev, ADRENO_REG_RBBM_SW_RESET_CMD, 1);
+ /*
+ * Do a dummy read to get a brief read cycle delay for the
+ * reset to take effect
+ */
+ adreno_readreg(adreno_dev, ADRENO_REG_RBBM_SW_RESET_CMD, ®);
+ adreno_writereg(adreno_dev, ADRENO_REG_RBBM_SW_RESET_CMD, 0);
+
+ /* Check VBIF status after reset */
+ if (timed_poll_check(device,
+ A6XX_RBBM_VBIF_GX_RESET_STATUS,
+ VBIF_RESET_ACK_MASK,
+ VBIF_RESET_ACK_TIMEOUT,
+ VBIF_RESET_ACK_MASK))
+ return -ETIMEDOUT;
+
+ a6xx_sptprac_enable(adreno_dev);
+
+ return 0;
+}
+
+#define A6XX_STATE_OF_CHILD (BIT(4) | BIT(5))
+#define A6XX_IDLE_FULL_LLM BIT(0)
+#define A6XX_WAKEUP_ACK BIT(1)
+#define A6XX_IDLE_FULL_ACK BIT(0)
+#define A6XX_VBIF_XIN_HALT_CTRL1_ACKS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
+static void a6xx_isense_disable(struct kgsl_device *device)
+{
+ unsigned int val;
+ const struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM))
+ return;
+
+ kgsl_gmu_regread(device, A6XX_GPU_CS_ENABLE_REG, &val);
+ if (val) {
+ kgsl_gmu_regwrite(device, A6XX_GPU_CS_ENABLE_REG, 0);
+ kgsl_gmu_regwrite(device, A6XX_GMU_ISENSE_CTRL, 0);
+ }
+}
+
+static int a6xx_llm_glm_handshake(struct kgsl_device *device)
+{
+ unsigned int val;
+ const struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct gmu_device *gmu = &device->gmu;
+
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM))
+ return 0;
+
+ kgsl_gmu_regread(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, &val);
+ if (!(val & A6XX_STATE_OF_CHILD)) {
+ kgsl_gmu_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0, BIT(4));
+ kgsl_gmu_regrmw(device, A6XX_GMU_LLM_GLM_SLEEP_CTRL, 0,
+ A6XX_IDLE_FULL_LLM);
+ if (timed_poll_check(device, A6XX_GMU_LLM_GLM_SLEEP_STATUS,
+ A6XX_IDLE_FULL_ACK, GPU_RESET_TIMEOUT,
+ A6XX_IDLE_FULL_ACK)) {
+ dev_err(&gmu->pdev->dev, "LLM-GLM handshake failed\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int a6xx_complete_rpmh_votes(struct kgsl_device *device)
+{
+ int ret = 0;
+
+ if (!kgsl_gmu_isenabled(device))
+ return ret;
+
+ ret |= timed_poll_check(device, A6XX_RSCC_TCS0_DRV0_STATUS, BIT(0),
+ GPU_RESET_TIMEOUT, BIT(0));
+ ret |= timed_poll_check(device, A6XX_RSCC_TCS1_DRV0_STATUS, BIT(0),
+ GPU_RESET_TIMEOUT, BIT(0));
+ ret |= timed_poll_check(device, A6XX_RSCC_TCS2_DRV0_STATUS, BIT(0),
+ GPU_RESET_TIMEOUT, BIT(0));
+ ret |= timed_poll_check(device, A6XX_RSCC_TCS3_DRV0_STATUS, BIT(0),
+ GPU_RESET_TIMEOUT, BIT(0));
+
+ return ret;
+}
+
+static int a6xx_gmu_suspend(struct kgsl_device *device)
+{
+ /* Max GX clients on A6xx is 2: GMU and KMD */
+ int ret = 0, max_client_num = 2;
+ struct gmu_device *gmu = &device->gmu;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* do it only if LM feature is enabled */
+ /* Disable ISENSE if it's on */
+ a6xx_isense_disable(device);
+
+ /* LLM-GLM handshake sequence */
+ a6xx_llm_glm_handshake(device);
+
+ /* If SPTP_RAC is on, turn off SPTP_RAC HS */
+ a6xx_sptprac_disable(adreno_dev);
+
+ /* Disconnect GPU from BUS. Clear and reconnected after reset */
+ adreno_vbif_clear_pending_transactions(device);
+ /* Unnecessary: a6xx_soft_reset(adreno_dev); */
+
+ /* Check no outstanding RPMh voting */
+ a6xx_complete_rpmh_votes(device);
+
+ if (gmu->idle_level < GPU_HW_IFPC) {
+ /* HM GDSC is controlled by KGSL */
+ ret = a6xx_hm_disable(ADRENO_DEVICE(device));
+ if (ret)
+ dev_err(&gmu->pdev->dev,
+ "suspend: fail: power off GPU HM\n");
+ } else if (gmu->gx_gdsc) {
+ if (regulator_is_enabled(gmu->gx_gdsc)) {
+ /* Switch gx gdsc control from GMU to CPU
+ * force non-zero reference count in clk driver
+ * so next disable call will turn
+ * off the GDSC
+ */
+ ret = regulator_enable(gmu->gx_gdsc);
+ if (ret)
+ dev_err(&gmu->pdev->dev,
+ "suspend fail: gx enable\n");
+
+ while ((max_client_num)) {
+ ret = regulator_disable(gmu->gx_gdsc);
+ if (!regulator_is_enabled(gmu->gx_gdsc))
+ break;
+ max_client_num -= 1;
+ }
+
+ if (!max_client_num)
+ dev_err(&gmu->pdev->dev,
+ "suspend fail: cannot disable gx\n");
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * a6xx_rpmh_gpu_pwrctrl() - GPU power control via RPMh/GMU interface
+ * @adreno_dev: Pointer to adreno device
+ * @mode: requested power mode
+ * @arg1: first argument for mode control
+ * @arg2: second argument for mode control
+ */
+static int a6xx_rpmh_gpu_pwrctrl(struct adreno_device *adreno_dev,
+ unsigned int mode, unsigned int arg1, unsigned int arg2)
+{
+ struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+ int ret;
+
+ switch (mode) {
+ case GMU_FW_START:
+ ret = a6xx_gmu_fw_start(device, arg1);
+ break;
+ case GMU_SUSPEND:
+ ret = a6xx_gmu_suspend(device);
+ break;
+ case GMU_FW_STOP:
+ ret = a6xx_rpmh_power_off_gpu(device);
+ break;
+ case GMU_DCVS_NOHFI:
+ ret = a6xx_gmu_dcvs_nohfi(device, arg1, arg2);
+ break;
+ case GMU_NOTIFY_SLUMBER:
+ ret = a6xx_notify_slumber(device);
+ break;
+ default:
+ dev_err(&gmu->pdev->dev,
+ "unsupported GMU power ctrl mode:%d\n", mode);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * a6xx_reset() - Helper function to reset the GPU
+ * @device: Pointer to the KGSL device structure for the GPU
+ * @fault: Type of fault. Needed to skip soft reset for MMU fault
+ *
+ * Try to reset the GPU to recover from a fault. First, try to do a low latency
+ * soft reset. If the soft reset fails for some reason, then bring out the big
+ * guns and toggle the footswitch.
+ */
+static int a6xx_reset(struct kgsl_device *device, int fault)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int ret = -EINVAL;
+ int i = 0;
+
+ /* Use the regular reset sequence for No GMU */
+ if (!kgsl_gmu_isenabled(device))
+ return adreno_reset(device, fault);
+
+ /* Transition from ACTIVE to RESET state */
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_RESET);
+
+ /* Try soft reset first */
+ if (!(fault & ADRENO_IOMMU_PAGE_FAULT)) {
+ int acked;
+
+ /* NMI */
+ kgsl_gmu_regwrite(device, A6XX_GMU_NMI_CONTROL_STATUS, 0);
+ kgsl_gmu_regwrite(device, A6XX_GMU_CM3_CFG, (1 << 9));
+
+ for (i = 0; i < 10; i++) {
+ kgsl_gmu_regread(device,
+ A6XX_GMU_NMI_CONTROL_STATUS, &acked);
+
+ /* NMI FW ACK recevied */
+ if (acked == 0x1)
+ break;
+
+ udelay(100);
+ }
+
+ if (acked)
+ ret = adreno_soft_reset(device);
+ if (ret)
+ KGSL_DEV_ERR_ONCE(device, "Device soft reset failed\n");
+ }
+ if (ret) {
+ /* If soft reset failed/skipped, then pull the power */
+ set_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv);
+ /* since device is officially off now clear start bit */
+ clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
+
+ /* Keep trying to start the device until it works */
+ for (i = 0; i < NUM_TIMES_RESET_RETRY; i++) {
+ ret = adreno_start(device, 0);
+ if (!ret)
+ break;
+
+ msleep(20);
+ }
+ }
+
+ clear_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv);
+
+ if (ret)
+ return ret;
+
+ if (i != 0)
+ KGSL_DRV_WARN(device, "Device hard reset tried %d tries\n", i);
+
+ /*
+ * If active_cnt is non-zero then the system was active before
+ * going into a reset - put it back in that state
+ */
+
+ if (atomic_read(&device->active_cnt))
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_ACTIVE);
+ else
+ kgsl_pwrctrl_change_state(device, KGSL_STATE_NAP);
+
+ return ret;
+}
+
static void a6xx_cp_hw_err_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -1671,7 +1929,6 @@
wmb();
}
-
/*
* a6xx_llc_configure_gpu_scid() - Program the sub-cache ID for all GPU blocks
* @adreno_dev: The adreno device pointer
@@ -2293,7 +2550,6 @@
A6XX_GMU_HOST2GMU_INTR_CLR),
ADRENO_REG_DEFINE(ADRENO_REG_GMU_HOST2GMU_INTR_RAW_INFO,
A6XX_GMU_HOST2GMU_INTR_RAW_INFO),
-
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TRUST_CONTROL,
A6XX_RBBM_SECVID_TRUST_CNTL),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE,
@@ -2337,4 +2593,6 @@
.hw_isidle = a6xx_hw_isidle, /* Replaced by NULL if GMU is disabled */
.wait_for_gmu_idle = a6xx_wait_for_gmu_idle,
.iommu_fault_block = a6xx_iommu_fault_block,
+ .reset = a6xx_reset,
+ .soft_reset = a6xx_soft_reset,
};
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index b831d0d..d01a5e9 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -2183,7 +2183,11 @@
kgsl_process_event_group(device, &hung_rb->events);
}
- ret = adreno_reset(device, fault);
+ if (gpudev->reset)
+ ret = gpudev->reset(device, fault);
+ else
+ ret = adreno_reset(device, fault);
+
mutex_unlock(&device->mutex);
/* if any other fault got in until reset then ignore */
atomic_set(&dispatcher->fault, 0);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index be379e3..ee4e7ef 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -40,6 +40,8 @@
* that the KGSL module believes a device is idle (has been inactive *
* past its timer) and all system resources are released. SUSPEND is *
* requested by the kernel and will be enforced upon all open devices. *
+ * RESET indicates that GPU or GMU hang happens. KGSL is handling *
+ * snapshot or recover GPU from hang. *
*/
#define KGSL_STATE_NONE 0x00000000
@@ -49,6 +51,7 @@
#define KGSL_STATE_SUSPEND 0x00000010
#define KGSL_STATE_AWARE 0x00000020
#define KGSL_STATE_SLUMBER 0x00000080
+#define KGSL_STATE_RESET 0x00000100
/**
* enum kgsl_event_results - result codes passed to an event callback when the
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 8de1a7e..f72b3fa 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -748,6 +748,7 @@
{
struct gmu_device *gmu = data;
struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int status = 0;
adreno_read_gmureg(ADRENO_DEVICE(device),
@@ -756,9 +757,12 @@
ADRENO_REG_GMU_AO_HOST_INTERRUPT_CLR, status);
/* Ignore GMU_INT_RSCC_COMP and GMU_INT_DBD WAKEUP interrupts */
- if (status & GMU_INT_WDOG_BITE)
+ if (status & GMU_INT_WDOG_BITE) {
dev_err_ratelimited(&gmu->pdev->dev,
"GMU watchdog expired interrupt received\n");
+ adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
+ adreno_dispatcher_schedule(device);
+ }
if (status & GMU_INT_HOST_AHB_BUS_ERR)
dev_err_ratelimited(&gmu->pdev->dev,
"AHB bus error interrupt received\n");
@@ -775,6 +779,7 @@
struct kgsl_hfi *hfi = data;
struct gmu_device *gmu = container_of(hfi, struct gmu_device, hfi);
struct kgsl_device *device = container_of(gmu, struct kgsl_device, gmu);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int status = 0;
adreno_read_gmureg(ADRENO_DEVICE(device),
@@ -784,9 +789,12 @@
if (status & HFI_IRQ_MSGQ_MASK)
tasklet_hi_schedule(&hfi->tasklet);
- if (status & HFI_IRQ_CM3_FAULT_MASK)
+ if (status & HFI_IRQ_CM3_FAULT_MASK) {
dev_err_ratelimited(&gmu->pdev->dev,
"GMU CM3 fault interrupt received\n");
+ adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
+ adreno_dispatcher_schedule(device);
+ }
if (status & ~HFI_IRQ_MASK)
dev_err_ratelimited(&gmu->pdev->dev,
"Unhandled HFI interrupts 0x%lx\n",
@@ -850,14 +858,6 @@
}
if (is_gmu) {
- if (!devm_request_mem_region(&gmu->pdev->dev, res->start,
- resource_size(res),
- res->name)) {
- dev_err(&gmu->pdev->dev,
- "GMU regs request mem region failed\n");
- return -ENOMEM;
- }
-
gmu->reg_phys = res->start;
gmu->reg_len = resource_size(res);
gmu->reg_virt = devm_ioremap(&gmu->pdev->dev, res->start,
@@ -1253,32 +1253,78 @@
return ret;
}
+static int gmu_fast_boot(struct kgsl_device *device)
+{
+ int ret;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+
+ hfi_stop(gmu);
+ clear_bit(GMU_HFI_ON, &gmu->flags);
+
+ ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
+ GMU_RESET, 0);
+ if (ret)
+ return ret;
+
+ /*FIXME: enabling WD interrupt*/
+
+ ret = hfi_start(gmu, GMU_WARM_BOOT);
+ if (ret)
+ return ret;
+
+ ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
+ OOB_CPINIT_CHECK_MASK, OOB_CPINIT_CLEAR_MASK);
+
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
+ gpudev->oob_clear(adreno_dev,
+ OOB_BOOT_SLUMBER_CLEAR_MASK);
+
+ return ret;
+}
+
+static int gmu_suspend(struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+ struct gmu_device *gmu = &device->gmu;
+
+ if (!test_bit(GMU_CLK_ON, &gmu->flags))
+ return 0;
+
+ /* Pending message in all queues are abandoned */
+ hfi_stop(gmu);
+ clear_bit(GMU_HFI_ON, &gmu->flags);
+ gmu_irq_disable(device);
+
+ if (gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_SUSPEND, 0, 0))
+ return -EINVAL;
+
+ gmu_disable_clks(gmu);
+ gmu_disable_gdsc(gmu);
+ return 0;
+}
+
/* To be called to power on both GPU and GMU */
int gmu_start(struct kgsl_device *device)
{
- int ret = 0;
+ int ret = 0, perf_idx;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct gmu_device *gmu = &device->gmu;
int bus_level = pwr->pwrlevels[pwr->default_pwrlevel].bus_freq;
- if (!kgsl_gmu_isenabled(device))
- return 0;
+ switch (device->state) {
+ case KGSL_STATE_INIT:
+ case KGSL_STATE_SUSPEND:
+ WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
+ gmu_enable_gdsc(gmu);
+ gmu_enable_clks(gmu);
- if (test_bit(GMU_CLK_ON, &gmu->flags))
- return 0;
-
- ret = gmu_enable_gdsc(gmu);
- if (ret)
- return ret;
-
- gmu_enable_clks(gmu);
-
- if (device->state == KGSL_STATE_INIT ||
- device->state == KGSL_STATE_SUSPEND) {
/* Convert to RPMh frequency index */
- int perf_idx = gmu->num_gpupwrlevels -
+ perf_idx = gmu->num_gpupwrlevels -
pwr->default_pwrlevel - 1;
/* Vote for 300MHz DDR for GMU to init */
@@ -1305,8 +1351,16 @@
ret = gmu_dcvs_set(gmu, perf_idx, bus_level);
if (ret)
goto error_gpu;
- } else {
- int perf_idx = gmu->num_gpupwrlevels - gmu->wakeup_pwrlevel - 1;
+
+ msm_bus_scale_client_update_request(gmu->pcl, 0);
+ break;
+
+ case KGSL_STATE_SLUMBER:
+ WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
+ gmu_enable_gdsc(gmu);
+ gmu_enable_clks(gmu);
+
+ perf_idx = gmu->num_gpupwrlevels - gmu->wakeup_pwrlevel - 1;
ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
GMU_WARM_BOOT, 0);
@@ -1325,6 +1379,46 @@
goto error_gpu;
gmu->wakeup_pwrlevel = pwr->default_pwrlevel;
}
+ break;
+
+ case KGSL_STATE_RESET:
+ if (test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv)) {
+ gmu_suspend(device);
+ gmu_enable_gdsc(gmu);
+ gmu_enable_clks(gmu);
+
+ perf_idx = gmu->num_gpupwrlevels -
+ pwr->active_pwrlevel - 1;
+
+ bus_level =
+ pwr->pwrlevels[pwr->active_pwrlevel].bus_freq;
+ ret = gpudev->rpmh_gpu_pwrctrl(
+ adreno_dev, GMU_FW_START, GMU_RESET, 0);
+ if (ret)
+ goto error_clks;
+
+ gmu_irq_enable(device);
+
+ ret = hfi_start(gmu, GMU_WARM_BOOT);
+ if (ret)
+ goto error_gpu;
+
+ /* Send DCVS level prior to reset*/
+ ret = gmu_dcvs_set(gmu, perf_idx, bus_level);
+ if (ret)
+ goto error_gpu;
+
+ ret = gpudev->oob_set(adreno_dev,
+ OOB_CPINIT_SET_MASK,
+ OOB_CPINIT_CHECK_MASK,
+ OOB_CPINIT_CLEAR_MASK);
+
+ } else {
+ gmu_fast_boot(device);
+ }
+ break;
+ default:
+ break;
}
/*
@@ -1332,30 +1426,20 @@
* In v2, this function call shall move ahead
* of hfi_start() to save power.
*/
+ if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
+ gpudev->oob_clear(adreno_dev,
+ OOB_BOOT_SLUMBER_CLEAR_MASK);
- if (device->state == KGSL_STATE_INIT ||
- device->state == KGSL_STATE_SUSPEND) {
- msm_bus_scale_client_update_request(gmu->pcl, 0);
- if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
- gpudev->oob_clear(adreno_dev,
- OOB_BOOT_SLUMBER_CLEAR_MASK);
- }
-
- return 0;
+ return ret;
error_gpu:
hfi_stop(gmu);
gmu_irq_disable(device);
- if (device->state == KGSL_STATE_INIT ||
- device->state == KGSL_STATE_SUSPEND) {
if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
gpudev->oob_clear(adreno_dev,
OOB_BOOT_SLUMBER_CLEAR_MASK);
- }
gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
error_bus:
- if (device->state == KGSL_STATE_INIT ||
- device->state == KGSL_STATE_SUSPEND)
msm_bus_scale_client_update_request(gmu->pcl, 0);
error_clks:
gmu_disable_clks(gmu);
@@ -1459,8 +1543,6 @@
if (gmu->reg_virt) {
devm_iounmap(&gmu->pdev->dev, gmu->reg_virt);
- devm_release_mem_region(&gmu->pdev->dev,
- gmu->reg_phys, gmu->reg_len);
gmu->reg_virt = NULL;
}
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index 4cfc120..a741beb 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -139,7 +139,7 @@
enum gmu_pwrctrl_mode {
GMU_FW_START,
GMU_FW_STOP,
- GMU_POWER_RESET,
+ GMU_SUSPEND,
GMU_DCVS_NOHFI,
GMU_NOTIFY_SLUMBER,
INVALID_POWER_CTRL
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 7811079..7ffb42b 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2493,6 +2493,8 @@
/* Force power on to do the stop */
status = kgsl_pwrctrl_enable(device);
case KGSL_STATE_ACTIVE:
+ /* fall through */
+ case KGSL_STATE_RESET:
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
del_timer_sync(&device->idle_timer);
kgsl_pwrscale_midframe_timer_cancel(device);
@@ -2595,6 +2597,11 @@
int status = 0;
switch (device->state) {
+ case KGSL_STATE_RESET:
+ if (!kgsl_gmu_isenabled(device))
+ break;
+ status = gmu_start(device);
+ break;
case KGSL_STATE_INIT:
status = kgsl_pwrctrl_enable(device);
break;
@@ -2645,6 +2652,7 @@
kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP);
/* fallthrough */
case KGSL_STATE_SLUMBER:
+ case KGSL_STATE_RESET:
break;
case KGSL_STATE_AWARE:
KGSL_PWR_WARN(device,
@@ -2787,6 +2795,8 @@
break;
case KGSL_STATE_SUSPEND:
status = _suspend(device);
+ case KGSL_STATE_RESET:
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_RESET);
break;
default:
KGSL_PWR_INFO(device, "bad state request 0x%x\n", state);
@@ -2838,6 +2848,8 @@
return "SUSPEND";
case KGSL_STATE_SLUMBER:
return "SLUMBER";
+ case KGSL_STATE_RESET:
+ return "RESET";
default:
break;
}
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index b5beea53..ab646a9 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -217,7 +217,15 @@
if (ret < 0 || value < 0)
ret = -EINVAL;
- return ret;
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->poll.report_id,
+ st->poll.index, sizeof(value), &value);
+ if (ret < 0 || value < 0)
+ return -EINVAL;
+
+ st->poll_interval = value;
+
+ return 0;
}
EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
@@ -259,7 +267,16 @@
if (ret < 0 || value < 0)
ret = -EINVAL;
- return ret;
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->sensitivity.report_id,
+ st->sensitivity.index, sizeof(value),
+ &value);
+ if (ret < 0 || value < 0)
+ return -EINVAL;
+
+ st->raw_hystersis = value;
+
+ return 0;
}
EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
@@ -355,6 +372,9 @@
/* Default unit of measure is milliseconds */
if (st->poll.units == 0)
st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;
+
+ st->poll_interval = -1;
+
return 0;
}
@@ -377,6 +397,8 @@
HID_USAGE_SENSOR_PROY_POWER_STATE,
&st->power_state);
+ st->raw_hystersis = -1;
+
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index ecf592d..6082934 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -51,6 +51,8 @@
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
+
+ poll_value = hid_sensor_read_poll_value(st);
} else {
int val;
@@ -87,9 +89,7 @@
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
sizeof(state_val), &state_val);
- if (state)
- poll_value = hid_sensor_read_poll_value(st);
- if (poll_value > 0)
+ if (state && poll_value)
msleep_interruptible(poll_value * 2);
return 0;
@@ -127,6 +127,20 @@
struct hid_sensor_common *attrb = container_of(work,
struct hid_sensor_common,
work);
+
+ if (attrb->poll_interval >= 0)
+ sensor_hub_set_feature(attrb->hsdev, attrb->poll.report_id,
+ attrb->poll.index,
+ sizeof(attrb->poll_interval),
+ &attrb->poll_interval);
+
+ if (attrb->raw_hystersis >= 0)
+ sensor_hub_set_feature(attrb->hsdev,
+ attrb->sensitivity.report_id,
+ attrb->sensitivity.index,
+ sizeof(attrb->raw_hystersis),
+ &attrb->raw_hystersis);
+
_hid_sensor_power_state(attrb, true);
}
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
index e690dd1..4b0f942 100644
--- a/drivers/iio/dac/ad7303.c
+++ b/drivers/iio/dac/ad7303.c
@@ -184,9 +184,9 @@
.address = (chan), \
.scan_type = { \
.sign = 'u', \
- .realbits = '8', \
- .storagebits = '8', \
- .shift = '0', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ .shift = 0, \
}, \
.ext_info = ad7303_ext_info, \
}
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index e5a533c..f762eb8 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -175,11 +175,12 @@
}
H6 = sign_extend32(tmp, 7);
- var = ((s32)data->t_fine) - 76800;
- var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var)) + 16384) >> 15)
- * (((((((var * H6) >> 10) * (((var * H3) >> 11) + 32768)) >> 10)
- + 2097152) * H2 + 8192) >> 14);
- var -= ((((var >> 15) * (var >> 15)) >> 7) * H1) >> 4;
+ var = ((s32)data->t_fine) - (s32)76800;
+ var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var))
+ + (s32)16384) >> 15) * (((((((var * H6) >> 10)
+ * (((var * (s32)H3) >> 11) + (s32)32768)) >> 10)
+ + (s32)2097152) * H2 + 8192) >> 14);
+ var -= ((((var >> 15) * (var >> 15)) >> 7) * (s32)H1) >> 4;
return var >> 12;
};
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index 5656deb..0204595 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -50,7 +50,6 @@
#define AS3935_TUNE_CAP 0x08
#define AS3935_CALIBRATE 0x3D
-#define AS3935_WRITE_DATA BIT(15)
#define AS3935_READ_DATA BIT(14)
#define AS3935_ADDRESS(x) ((x) << 8)
@@ -105,7 +104,7 @@
{
u8 *buf = st->buf;
- buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8;
+ buf[0] = AS3935_ADDRESS(reg) >> 8;
buf[1] = val;
return spi_write(st->spi, buf, 2);
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 0f58f46..8fd108d 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -444,8 +444,8 @@
fl6.saddr = src_in->sin6_addr;
fl6.flowi6_oif = addr->bound_dev_if;
- dst = ip6_route_output(addr->net, NULL, &fl6);
- if ((ret = dst->error))
+ ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6);
+ if (ret < 0)
goto put;
rt = (struct rt6_info *)dst;
diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c
index bd786b7..bb72976 100644
--- a/drivers/infiniband/hw/hfi1/file_ops.c
+++ b/drivers/infiniband/hw/hfi1/file_ops.c
@@ -751,6 +751,9 @@
/* release the cpu */
hfi1_put_proc_affinity(fdata->rec_cpu_num);
+ /* clean up rcv side */
+ hfi1_user_exp_rcv_free(fdata);
+
/*
* Clear any left over, unhandled events so the next process that
* gets this context doesn't get confused.
@@ -790,7 +793,7 @@
dd->rcd[uctxt->ctxt] = NULL;
- hfi1_user_exp_rcv_free(fdata);
+ hfi1_user_exp_rcv_grp_free(uctxt);
hfi1_clear_ctxt_pkey(dd, uctxt->ctxt);
uctxt->rcvwait_to = 0;
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index e3b5bc9..34cfd34 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -1757,6 +1757,7 @@
!HFI1_CAP_KGET_MASK(rcd->flags, MULTI_PKT_EGR)) {
dd_dev_err(dd, "ctxt%u: Failed to allocate eager buffers\n",
rcd->ctxt);
+ ret = -ENOMEM;
goto bail_rcvegrbuf_phys;
}
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
index 64d2652..db0f140 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
@@ -250,36 +250,40 @@
return ret;
}
+void hfi1_user_exp_rcv_grp_free(struct hfi1_ctxtdata *uctxt)
+{
+ struct tid_group *grp, *gptr;
+
+ list_for_each_entry_safe(grp, gptr, &uctxt->tid_group_list.list,
+ list) {
+ list_del_init(&grp->list);
+ kfree(grp);
+ }
+ hfi1_clear_tids(uctxt);
+}
+
int hfi1_user_exp_rcv_free(struct hfi1_filedata *fd)
{
struct hfi1_ctxtdata *uctxt = fd->uctxt;
- struct tid_group *grp, *gptr;
- if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags))
- return 0;
/*
* The notifier would have been removed when the process'es mm
* was freed.
*/
- if (fd->handler)
+ if (fd->handler) {
hfi1_mmu_rb_unregister(fd->handler);
-
- kfree(fd->invalid_tids);
-
- if (!uctxt->cnt) {
+ } else {
if (!EXP_TID_SET_EMPTY(uctxt->tid_full_list))
unlock_exp_tids(uctxt, &uctxt->tid_full_list, fd);
if (!EXP_TID_SET_EMPTY(uctxt->tid_used_list))
unlock_exp_tids(uctxt, &uctxt->tid_used_list, fd);
- list_for_each_entry_safe(grp, gptr, &uctxt->tid_group_list.list,
- list) {
- list_del_init(&grp->list);
- kfree(grp);
- }
- hfi1_clear_tids(uctxt);
}
+ kfree(fd->invalid_tids);
+ fd->invalid_tids = NULL;
+
kfree(fd->entry_to_rb);
+ fd->entry_to_rb = NULL;
return 0;
}
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
index 9bc8d9f..d1d7d3d 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
@@ -70,6 +70,7 @@
(tid) |= EXP_TID_SET(field, (value)); \
} while (0)
+void hfi1_user_exp_rcv_grp_free(struct hfi1_ctxtdata *uctxt);
int hfi1_user_exp_rcv_init(struct file *);
int hfi1_user_exp_rcv_free(struct hfi1_filedata *);
int hfi1_user_exp_rcv_setup(struct file *, struct hfi1_tid_info *);
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index be2d02b..1fb31a4 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -1828,7 +1828,7 @@
klms[i].va = cpu_to_be64(sg_dma_address(sg) + sg_offset);
klms[i].bcount = cpu_to_be32(sg_dma_len(sg) - sg_offset);
klms[i].key = cpu_to_be32(lkey);
- mr->ibmr.length += sg_dma_len(sg);
+ mr->ibmr.length += sg_dma_len(sg) - sg_offset;
sg_offset = 0;
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index dd96670..b91a6b5 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -411,6 +411,7 @@
#define ARM_SMMU_OPT_FATAL_ASF (1 << 1)
#define ARM_SMMU_OPT_SKIP_INIT (1 << 2)
#define ARM_SMMU_OPT_DYNAMIC (1 << 3)
+#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
u32 options;
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
@@ -529,6 +530,7 @@
{ ARM_SMMU_OPT_FATAL_ASF, "qcom,fatal-asf" },
{ ARM_SMMU_OPT_SKIP_INIT, "qcom,skip-init" },
{ ARM_SMMU_OPT_DYNAMIC, "qcom,dynamic" },
+ { ARM_SMMU_OPT_3LVL_TABLES, "qcom,use-3-lvl-tables" },
{ 0, NULL},
};
@@ -1567,6 +1569,8 @@
oas = smmu->ipa_size;
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) {
fmt = ARM_64_LPAE_S1;
+ if (smmu->options & ARM_SMMU_OPT_3LVL_TABLES)
+ ias = min(ias, 39UL);
} else if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_L) {
fmt = ARM_32_LPAE_S1;
ias = min(ias, 32UL);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index b9e50c1..87fcbf7 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2049,11 +2049,14 @@
if (context_copied(context)) {
u16 did_old = context_domain_id(context);
- if (did_old >= 0 && did_old < cap_ndoms(iommu->cap))
+ if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) {
iommu->flush.flush_context(iommu, did_old,
(((u16)bus) << 8) | devfn,
DMA_CCMD_MASK_NOBIT,
DMA_CCMD_DEVICE_INVL);
+ iommu->flush.flush_iotlb(iommu, did_old, 0, 0,
+ DMA_TLB_DSI_FLUSH);
+ }
}
pgd = domain->pgd;
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 41515bb..ee50a61 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -299,3 +299,5 @@
config STM32_EXTI
bool
select IRQ_DOMAIN
+
+source "drivers/irqchip/qcom/Kconfig"
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 987bd89..450059c 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -75,3 +75,4 @@
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
+obj-$(CONFIG_QTI_PDC) += qcom/
diff --git a/drivers/irqchip/qcom/Kconfig b/drivers/irqchip/qcom/Kconfig
new file mode 100644
index 0000000..e4a7a88
--- /dev/null
+++ b/drivers/irqchip/qcom/Kconfig
@@ -0,0 +1,15 @@
+config QTI_PDC
+ bool "QTI PDC"
+ depends on ARCH_QCOM
+ select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ QTI Power Domain Controller driver to manage and configure wakeup
+ IRQs
+
+config QTI_PDC_SDM845
+ bool "QTI PDC SDM845"
+ select QTI_PDC
+ default y if ARCH_SDM845
+ help
+ QTI Power Domain Controller for SDM845
diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile
new file mode 100644
index 0000000..1b7856d
--- /dev/null
+++ b/drivers/irqchip/qcom/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QTI_PDC) += pdc.o
+obj-$(CONFIG_QTI_PDC_SDM845) += pdc-sdm845.o
diff --git a/drivers/irqchip/qcom/pdc-sdm845.c b/drivers/irqchip/qcom/pdc-sdm845.c
new file mode 100644
index 0000000..178cf1f0
--- /dev/null
+++ b/drivers/irqchip/qcom/pdc-sdm845.c
@@ -0,0 +1,139 @@
+/* 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.
+ *
+ */
+
+#include <linux/irqchip.h>
+#include "pdc.h"
+
+static struct pdc_pin sdm845_data[] = {
+ {0, 512}, /* rpmh_wake */
+ {1, 513}, /* ee0_apps_hlos_spmi_periph_irq */
+ {2, 514}, /* ee1_apps_trustzone_spmi_periph_irq */
+ {3, 515}, /* secure_wdog_expired */
+ {4, 516}, /* secure_wdog_bark_irq */
+ {5, 517}, /* aop_wdog_expired_irq */
+ {6, 518}, /* qmp_usb3_lfps_rxterm_irq */
+ {7, 519}, /* qmp_usb3_lfps_rxterm_irq */
+ {8, 520}, /* eud_p0_dmse_int_mx */
+ {9, 521}, /* eud_p0_dpse_int_mx */
+ {10, 522}, /* eud_p1_dmse_int_mx */
+ {11, 523}, /* eud_p1_dpse_int_mx */
+ {12, 524}, /* eud_int_mx[1] */
+ {13, 525}, /* ssc_xpu_irq_summary */
+ {14, 526}, /* wd_bite_apps */
+ {15, 527}, /* ssc_vmidmt_irq_summary */
+ {16, 528}, /* q6ss_irq_out_apps_ipc[4] */
+ {17, 529}, /* not-connected */
+ {18, 530}, /* aoss_pmic_arb_mpu_xpu_summary_irq */
+ {19, 531}, /* apps_pdc_irq_in_19 */
+ {20, 532}, /* apps_pdc_irq_in_20 */
+ {21, 533}, /* apps_pdc_irq_in_21 */
+ {22, 534}, /* pdc_apps_epcb_timeout_summary_irq */
+ {23, 535}, /* spmi_protocol_irq */
+ {24, 536}, /* tsense0_tsense_max_min_int */
+ {25, 537}, /* tsense1_tsense_max_min_int */
+ {26, 538}, /* tsense0_upper_lower_intr */
+ {27, 539}, /* tsense1_upper_lower_intr */
+ {28, 540}, /* tsense0_critical_intr */
+ {29, 541}, /* tsense1_critical_intr */
+ {30, 542}, /* core_bi_px_gpio_1 */
+ {31, 543}, /* core_bi_px_gpio_3 */
+ {32, 544}, /* core_bi_px_gpio_5 */
+ {33, 545}, /* core_bi_px_gpio_10 */
+ {34, 546}, /* core_bi_px_gpio_11 */
+ {35, 547}, /* core_bi_px_gpio_20 */
+ {36, 548}, /* core_bi_px_gpio_22 */
+ {37, 549}, /* core_bi_px_gpio_24 */
+ {38, 550}, /* core_bi_px_gpio_26 */
+ {39, 551}, /* core_bi_px_gpio_30 */
+ {41, 553}, /* core_bi_px_gpio_32 */
+ {42, 554}, /* core_bi_px_gpio_34 */
+ {43, 555}, /* core_bi_px_gpio_36 */
+ {44, 556}, /* core_bi_px_gpio_37 */
+ {45, 557}, /* core_bi_px_gpio_38 */
+ {46, 558}, /* core_bi_px_gpio_39 */
+ {47, 559}, /* core_bi_px_gpio_40 */
+ {49, 561}, /* core_bi_px_gpio_43 */
+ {50, 562}, /* core_bi_px_gpio_44 */
+ {51, 563}, /* core_bi_px_gpio_46 */
+ {52, 564}, /* core_bi_px_gpio_48 */
+ {54, 566}, /* core_bi_px_gpio_52 */
+ {55, 567}, /* core_bi_px_gpio_53 */
+ {56, 568}, /* core_bi_px_gpio_54 */
+ {57, 569}, /* core_bi_px_gpio_56 */
+ {58, 570}, /* core_bi_px_gpio_57 */
+ {59, 571}, /* core_bi_px_gpio_58 */
+ {60, 572}, /* core_bi_px_gpio_59 */
+ {61, 573}, /* core_bi_px_gpio_60 */
+ {62, 574}, /* core_bi_px_gpio_61 */
+ {63, 575}, /* core_bi_px_gpio_62 */
+ {64, 576}, /* core_bi_px_gpio_63 */
+ {65, 577}, /* core_bi_px_gpio_64 */
+ {66, 578}, /* core_bi_px_gpio_66 */
+ {67, 579}, /* core_bi_px_gpio_68 */
+ {68, 580}, /* core_bi_px_gpio_71 */
+ {69, 581}, /* core_bi_px_gpio_73 */
+ {70, 582}, /* core_bi_px_gpio_77 */
+ {71, 583}, /* core_bi_px_gpio_78 */
+ {72, 584}, /* core_bi_px_gpio_79 */
+ {73, 585}, /* core_bi_px_gpio_80 */
+ {74, 586}, /* core_bi_px_gpio_84 */
+ {75, 587}, /* core_bi_px_gpio_85 */
+ {76, 588}, /* core_bi_px_gpio_86 */
+ {77, 589}, /* core_bi_px_gpio_88 */
+ {79, 591}, /* core_bi_px_gpio_91 */
+ {80, 592}, /* core_bi_px_gpio_92 */
+ {81, 593}, /* core_bi_px_gpio_95 */
+ {82, 594}, /* core_bi_px_gpio_96 */
+ {83, 595}, /* core_bi_px_gpio_97 */
+ {84, 596}, /* core_bi_px_gpio_101 */
+ {85, 597}, /* core_bi_px_gpio_103 */
+ {86, 598}, /* core_bi_px_gpio_104 */
+ {87, 599}, /* core_bi_px_to_mpm[6] */
+ {88, 600}, /* core_bi_px_to_mpm[0] */
+ {89, 601}, /* core_bi_px_to_mpm[1] */
+ {90, 602}, /* core_bi_px_gpio_115 */
+ {91, 603}, /* core_bi_px_gpio_116 */
+ {92, 604}, /* core_bi_px_gpio_117 */
+ {93, 605}, /* core_bi_px_gpio_118 */
+ {94, 641}, /* core_bi_px_gpio_119 */
+ {95, 642}, /* core_bi_px_gpio_120 */
+ {96, 643}, /* core_bi_px_gpio_121 */
+ {97, 644}, /* core_bi_px_gpio_122 */
+ {98, 645}, /* core_bi_px_gpio_123 */
+ {99, 646}, /* core_bi_px_gpio_124 */
+ {100, 647}, /* core_bi_px_gpio_125 */
+ {101, 648}, /* core_bi_px_to_mpm[5] */
+ {102, 649}, /* core_bi_px_gpio_127 */
+ {103, 650}, /* core_bi_px_gpio_128 */
+ {104, 651}, /* core_bi_px_gpio_129 */
+ {105, 652}, /* core_bi_px_gpio_130 */
+ {106, 653}, /* core_bi_px_gpio_132 */
+ {107, 654}, /* core_bi_px_gpio_133 */
+ {108, 655}, /* core_bi_px_gpio_145 */
+ {119, 666}, /* core_bi_px_to_mpm[2] */
+ {120, 667}, /* core_bi_px_to_mpm[3] */
+ {121, 668}, /* core_bi_px_to_mpm[4] */
+ {122, 669}, /* core_bi_px_gpio_41 */
+ {123, 670}, /* core_bi_px_gpio_89 */
+ {124, 671}, /* core_bi_px_gpio_31 */
+ {125, 672}, /* core_bi_px_gpio_49 */
+ {-1}
+};
+
+static int __init qcom_pdc_gic_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return qcom_pdc_init(node, parent, sdm845_data);
+}
+
+IRQCHIP_DECLARE(pdc_sdm845, "qcom,pdc-sdm845", qcom_pdc_gic_init);
diff --git a/drivers/irqchip/qcom/pdc.c b/drivers/irqchip/qcom/pdc.c
new file mode 100644
index 0000000..923552f
--- /dev/null
+++ b/drivers/irqchip/qcom/pdc.c
@@ -0,0 +1,299 @@
+/* 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include "pdc.h"
+#define CREATE_TRACE_POINTS
+#include "trace/events/pdc.h"
+
+#define MAX_IRQS 126
+#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
+#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
+
+enum pdc_register_offsets {
+ IRQ_ENABLE_BANK = 0x10,
+ IRQ_i_CFG = 0x110,
+};
+
+static DEFINE_SPINLOCK(pdc_lock);
+static void __iomem *pdc_base;
+
+static int get_pdc_pin(irq_hw_number_t hwirq, void *data)
+{
+ int i;
+ struct pdc_pin *pdc_data = (struct pdc_pin *) data;
+
+ for (i = 0; pdc_data[i].pin >= 0; i++) {
+ if (pdc_data[i].hwirq == hwirq)
+ return pdc_data[i].pin;
+ }
+
+ return -EINVAL;
+}
+
+static inline int pdc_enable_intr(struct irq_data *d, bool on)
+{
+ int pin_out = get_pdc_pin(d->hwirq, d->chip_data);
+ unsigned int index, mask;
+ u32 enable, r_enable;
+ unsigned long flags;
+
+ if (pin_out < 0)
+ return 0;
+
+ index = pin_out / 32;
+ mask = pin_out % 32;
+ spin_lock_irqsave(&pdc_lock, flags);
+
+ enable = readl_relaxed(pdc_base + IRQ_ENABLE_BANK + (index *
+ sizeof(uint32_t)));
+ if (on)
+ enable = ENABLE_INTR(enable, mask);
+ else
+ enable = CLEAR_INTR(enable, mask);
+
+ writel_relaxed(enable, pdc_base + IRQ_ENABLE_BANK + (index *
+ sizeof(uint32_t)));
+
+ do {
+ r_enable = readl_relaxed(pdc_base + IRQ_ENABLE_BANK +
+ (index * sizeof(uint32_t)));
+ if (r_enable == enable)
+ break;
+ udelay(5);
+ } while (1);
+
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
+ trace_irq_pin_config("enable", (u32)pin_out, (u32)d->hwirq,
+ 0, on);
+
+ return 0;
+}
+
+static void qcom_pdc_gic_mask(struct irq_data *d)
+{
+ pdc_enable_intr(d, false);
+ irq_chip_mask_parent(d);
+}
+
+static void qcom_pdc_gic_unmask(struct irq_data *d)
+{
+ pdc_enable_intr(d, true);
+ irq_chip_unmask_parent(d);
+}
+
+static void qcom_pdc_gic_enable(struct irq_data *d)
+{
+ pdc_enable_intr(d, true);
+ irq_chip_enable_parent(d);
+}
+
+static void qcom_pdc_gic_disable(struct irq_data *d)
+{
+ pdc_enable_intr(d, false);
+ irq_chip_disable_parent(d);
+}
+
+/*
+ * GIC does not handle falling edge or active low. To allow falling edge and
+ * active low interrupts to be handled at GIC, PDC has an inverter that inverts
+ * falling edge into a rising edge and active low into an active high.
+ * For the inverter to work, the polarity bit in the IRQ_CONFIG register has to
+ * set as per the table below.
+ * (polarity, falling edge, rising edge ) ORIG POL CONV POLARITY
+ * 3'b0 00 Level sensitive active low (~~~|_____) (___|~~~~~) LOW
+ * 3'b0 01 Rising edge sensitive (___|~~|__) (~~~|__|~~) NOT USED
+ * 3'b0 10 Falling edge sensitive (~~~|__|~~) (___|~~|__) LOW
+ * 3'b0 11 Dual Edge sensitive NOT USED
+ * 3'b1 00 Level senstive active High (___|~~~~~) (___|~~~~~) HIGH
+ * 3'b1 01 Falling Edge sensitive (~~~|__|~~) (~~~|__|~~) NOT USED
+ * 3'b1 10 Rising edge sensitive (___|~~|__) (___|~~|__) HIGH
+ * 3'b1 11 Dual Edge sensitive HIGH
+ */
+enum pdc_irq_config_bits {
+ POLARITY_LOW = 0, //0 00
+ FALLING_EDGE = 2, //0 10
+ POLARITY_HIGH = 4,//1 00
+ RISING_EDGE = 6, //1 10
+ DUAL_EDGE = 7, //1 11
+};
+
+static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
+{
+ int pin_out = get_pdc_pin(d->hwirq, d->chip_data);
+ u32 pdc_type = 0, config;
+
+ if (pin_out < 0)
+ goto fwd_to_parent;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ pdc_type = RISING_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pdc_type = FALLING_EDGE;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ pdc_type = DUAL_EDGE;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pdc_type = POLARITY_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pdc_type = POLARITY_LOW;
+ break;
+ default:
+ pdc_type = POLARITY_HIGH;
+ break;
+ }
+ writel_relaxed(pdc_type, pdc_base + IRQ_i_CFG +
+ (pin_out * sizeof(uint32_t)));
+
+ do {
+ config = readl_relaxed(pdc_base + IRQ_i_CFG +
+ (pin_out * sizeof(uint32_t)));
+ if (config == pdc_type)
+ break;
+ udelay(5);
+ } while (1);
+
+ trace_irq_pin_config("type_config", (u32)pin_out, (u32)d->hwirq,
+ pdc_type, 0);
+
+ /*
+ * If type is edge triggered, forward that as Rising edge as PDC
+ * takes care of converting falling edge to rising edge signal
+ */
+ if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
+ type = IRQ_TYPE_EDGE_RISING;
+
+ /*
+ * If type is level, then forward that as level high as PDC
+ * takes care of converting falling edge to rising edge signal
+ */
+ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ type = IRQ_TYPE_LEVEL_HIGH;
+
+fwd_to_parent:
+
+ return irq_chip_set_type_parent(d, type);
+}
+
+static struct irq_chip qcom_pdc_gic_chip = {
+ .name = "PDC-GIC",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = qcom_pdc_gic_mask,
+ .irq_enable = qcom_pdc_gic_enable,
+ .irq_unmask = qcom_pdc_gic_unmask,
+ .irq_disable = qcom_pdc_gic_disable,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = qcom_pdc_gic_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+ .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+#endif
+};
+
+static int qcom_pdc_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type)
+{
+ return d->parent->ops->translate(d->parent, fwspec, hwirq, type);
+}
+
+static int qcom_pdc_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs, void *data)
+{
+ struct irq_fwspec *fwspec = data;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq;
+ int i;
+ unsigned int type;
+ int ret;
+
+ ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return -EINVAL;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &qcom_pdc_gic_chip, domain->host_data);
+
+ parent_fwspec = *fwspec;
+ parent_fwspec.fwnode = domain->parent->fwnode;
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops qcom_pdc_ops = {
+ .translate = qcom_pdc_translate,
+ .alloc = qcom_pdc_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+int qcom_pdc_init(struct device_node *node,
+ struct device_node *parent, void *data)
+{
+ struct irq_domain *parent_domain;
+ int ret;
+ struct irq_domain *pdc_domain;
+
+ pdc_base = of_iomap(node, 0);
+ if (!pdc_base) {
+ pr_err("%s(): unable to map PDC registers\n", node->full_name);
+ return -ENXIO;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("unable to obtain PDC parent domain\n");
+ ret = -ENXIO;
+ goto failure;
+ }
+
+ pdc_domain = irq_domain_add_hierarchy(parent_domain, 0, MAX_IRQS,
+ node, &qcom_pdc_ops, data);
+ if (!pdc_domain) {
+ pr_err("GIC domain add failed\n");
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ pdc_domain->name = "qcom,pdc";
+
+ return 0;
+
+failure:
+ iounmap(pdc_base);
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_pdc_init);
diff --git a/drivers/irqchip/qcom/pdc.h b/drivers/irqchip/qcom/pdc.h
new file mode 100644
index 0000000..7c4d89c
--- /dev/null
+++ b/drivers/irqchip/qcom/pdc.h
@@ -0,0 +1,23 @@
+/* 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.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/device.h>
+
+struct pdc_pin {
+ int pin;
+ irq_hw_number_t hwirq;
+};
+
+int qcom_pdc_init(struct device_node *node,
+ struct device_node *parent, void *data);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 3b53f34..e7b8f49 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -357,6 +357,7 @@
config DM_RAID
tristate "RAID 1/4/5/6/10 target"
depends on BLK_DEV_DM
+ select MD_RAID0
select MD_RAID1
select MD_RAID10
select MD_RAID456
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 125aedc..8bf9667 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -215,7 +215,7 @@
* Buffers are freed after this timeout
*/
static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
-static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
+static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
static unsigned long dm_bufio_peak_allocated;
static unsigned long dm_bufio_allocated_kmem_cache;
@@ -923,10 +923,11 @@
{
unsigned long buffers;
- if (ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch) {
- mutex_lock(&dm_bufio_clients_lock);
- __cache_size_refresh();
- mutex_unlock(&dm_bufio_clients_lock);
+ if (unlikely(ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch)) {
+ if (mutex_trylock(&dm_bufio_clients_lock)) {
+ __cache_size_refresh();
+ mutex_unlock(&dm_bufio_clients_lock);
+ }
}
buffers = dm_bufio_cache_size_per_client >>
@@ -1540,10 +1541,10 @@
return true;
}
-static unsigned get_retain_buffers(struct dm_bufio_client *c)
+static unsigned long get_retain_buffers(struct dm_bufio_client *c)
{
- unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
- return retain_bytes / c->block_size;
+ unsigned long retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
+ return retain_bytes >> (c->sectors_per_block_bits + SECTOR_SHIFT);
}
static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
@@ -1553,7 +1554,7 @@
struct dm_buffer *b, *tmp;
unsigned long freed = 0;
unsigned long count = nr_to_scan;
- unsigned retain_target = get_retain_buffers(c);
+ unsigned long retain_target = get_retain_buffers(c);
for (l = 0; l < LIST_SIZE; l++) {
list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) {
@@ -1779,11 +1780,19 @@
static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
{
struct dm_buffer *b, *tmp;
- unsigned retain_target = get_retain_buffers(c);
- unsigned count;
+ unsigned long retain_target = get_retain_buffers(c);
+ unsigned long count;
+ LIST_HEAD(write_list);
dm_bufio_lock(c);
+ __check_watermark(c, &write_list);
+ if (unlikely(!list_empty(&write_list))) {
+ dm_bufio_unlock(c);
+ __flush_write_list(&write_list);
+ dm_bufio_lock(c);
+ }
+
count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) {
if (count <= retain_target)
@@ -1808,6 +1817,8 @@
mutex_lock(&dm_bufio_clients_lock);
+ __cache_size_refresh();
+
list_for_each_entry(c, &dm_bufio_all_clients, client_list)
__evict_old_buffers(c, max_age_hz);
@@ -1930,7 +1941,7 @@
module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
-module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR);
+module_param_named(retain_bytes, dm_bufio_retain_bytes, ulong, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory");
module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 6955778..6937ca4 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -1383,17 +1383,19 @@
int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown)
{
- int r;
+ int r = -EINVAL;
flags_mutator mutator = (clean_shutdown ? set_clean_shutdown :
clear_clean_shutdown);
WRITE_LOCK(cmd);
+ if (cmd->fail_io)
+ goto out;
+
r = __commit_transaction(cmd, mutator);
if (r)
goto out;
r = __begin_transaction(cmd);
-
out:
WRITE_UNLOCK(cmd);
return r;
@@ -1405,7 +1407,8 @@
int r = -EINVAL;
READ_LOCK(cmd);
- r = dm_sm_get_nr_free(cmd->metadata_sm, result);
+ if (!cmd->fail_io)
+ r = dm_sm_get_nr_free(cmd->metadata_sm, result);
READ_UNLOCK(cmd);
return r;
@@ -1417,7 +1420,8 @@
int r = -EINVAL;
READ_LOCK(cmd);
- r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
+ if (!cmd->fail_io)
+ r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
READ_UNLOCK(cmd);
return r;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index e477af8..ac8235b 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -119,7 +119,8 @@
static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
static void trigger_event(struct work_struct *work);
-static void activate_path(struct work_struct *work);
+static void activate_or_offline_path(struct pgpath *pgpath);
+static void activate_path_work(struct work_struct *work);
static void process_queued_bios(struct work_struct *work);
/*-----------------------------------------------
@@ -144,7 +145,7 @@
if (pgpath) {
pgpath->is_active = true;
- INIT_DELAYED_WORK(&pgpath->activate_path, activate_path);
+ INIT_DELAYED_WORK(&pgpath->activate_path, activate_path_work);
}
return pgpath;
@@ -1515,10 +1516,8 @@
spin_unlock_irqrestore(&m->lock, flags);
}
-static void activate_path(struct work_struct *work)
+static void activate_or_offline_path(struct pgpath *pgpath)
{
- struct pgpath *pgpath =
- container_of(work, struct pgpath, activate_path.work);
struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
if (pgpath->is_active && !blk_queue_dying(q))
@@ -1527,6 +1526,14 @@
pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
}
+static void activate_path_work(struct work_struct *work)
+{
+ struct pgpath *pgpath =
+ container_of(work, struct pgpath, activate_path.work);
+
+ activate_or_offline_path(pgpath);
+}
+
static int noretry_error(int error)
{
switch (error) {
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index a15091a..4477bf9 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -485,11 +485,11 @@
if (r < 0)
return r;
- r = save_sm_roots(pmd);
+ r = dm_tm_pre_commit(pmd->tm);
if (r < 0)
return r;
- r = dm_tm_pre_commit(pmd->tm);
+ r = save_sm_roots(pmd);
if (r < 0)
return r;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 24925f2..eddd360 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6752,6 +6752,7 @@
void __user *argp = (void __user *)arg;
struct mddev *mddev = NULL;
int ro;
+ bool did_set_md_closing = false;
if (!md_ioctl_valid(cmd))
return -ENOTTY;
@@ -6841,7 +6842,9 @@
err = -EBUSY;
goto out;
}
+ WARN_ON_ONCE(test_bit(MD_CLOSING, &mddev->flags));
set_bit(MD_CLOSING, &mddev->flags);
+ did_set_md_closing = true;
mutex_unlock(&mddev->open_mutex);
sync_blockdev(bdev);
}
@@ -7041,6 +7044,8 @@
mddev->hold_active = 0;
mddev_unlock(mddev);
out:
+ if(did_set_md_closing)
+ clear_bit(MD_CLOSING, &mddev->flags);
return err;
}
#ifdef CONFIG_COMPAT
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index 20a4032..7a75b50 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -897,8 +897,12 @@
else
*result_key = le64_to_cpu(ro_node(s)->keys[0]);
- if (next_block || flags & INTERNAL_NODE)
- block = value64(ro_node(s), i);
+ if (next_block || flags & INTERNAL_NODE) {
+ if (find_highest)
+ block = value64(ro_node(s), i);
+ else
+ block = value64(ro_node(s), 0);
+ }
} while (flags & INTERNAL_NODE);
diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c
index ebb280a..32adf6b 100644
--- a/drivers/md/persistent-data/dm-space-map-disk.c
+++ b/drivers/md/persistent-data/dm-space-map-disk.c
@@ -142,10 +142,23 @@
static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b)
{
+ int r;
+ uint32_t old_count;
enum allocation_event ev;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
- return sm_ll_dec(&smd->ll, b, &ev);
+ r = sm_ll_dec(&smd->ll, b, &ev);
+ if (!r && (ev == SM_FREE)) {
+ /*
+ * It's only free if it's also free in the last
+ * transaction.
+ */
+ r = sm_ll_lookup(&smd->old_ll, b, &old_count);
+ if (!r && !old_count)
+ smd->nr_allocated_this_transaction--;
+ }
+
+ return r;
}
static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index cce6057b..f34ad2b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2253,6 +2253,10 @@
err = -ENOMEM;
mutex_unlock(&conf->cache_size_mutex);
+
+ conf->slab_cache = sc;
+ conf->active_name = 1-conf->active_name;
+
/* Step 4, return new stripes to service */
while(!list_empty(&newstripes)) {
nsh = list_entry(newstripes.next, struct stripe_head, lru);
@@ -2270,8 +2274,6 @@
}
/* critical section pass, GFP_NOIO no longer needed */
- conf->slab_cache = sc;
- conf->active_name = 1-conf->active_name;
if (!err)
conf->pool_size = newsize;
return err;
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
index 5afb9c5..fd0f25e 100644
--- a/drivers/media/dvb-frontends/cxd2841er.c
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -3852,7 +3852,9 @@
FE_CAN_MUTE_TS |
FE_CAN_2G_MODULATION,
.frequency_min = 42000000,
- .frequency_max = 1002000000
+ .frequency_max = 1002000000,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000
},
.init = cxd2841er_init_tc,
.sleep = cxd2841er_sleep_tc,
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 76dd1f3..5a4e6e9 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -59,15 +59,22 @@
}
if (!bubble_state) {
- CDBG("%s: Sync success: fd 0x%x\n", __func__,
+ CDBG("%s: Sync with success: fd 0x%x\n", __func__,
req_isp->fence_map_out[j].sync_id);
- cam_sync_signal(req_isp->fence_map_out[j].sync_id,
+ rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
CAM_SYNC_STATE_SIGNALED_SUCCESS);
+ if (rc)
+ pr_err("%s: Sync failed with rc = %d\n",
+ __func__, rc);
+
} else if (!req_isp->bubble_report) {
- CDBG("%s: Sync failure: fd 0x%x\n", __func__,
+ CDBG("%s: Sync with failure: fd 0x%x\n", __func__,
req_isp->fence_map_out[j].sync_id);
- cam_sync_signal(req_isp->fence_map_out[j].sync_id,
+ rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
CAM_SYNC_STATE_SIGNALED_ERROR);
+ if (rc)
+ pr_err("%s: Sync failed with rc = %d\n",
+ __func__, rc);
} else {
/*
* Ignore the buffer done if bubble detect is on
@@ -277,7 +284,7 @@
ctx_isp->frame_id++;
ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
- pr_err("%s: next substate %d\n", __func__,
+ CDBG("%s: next substate %d\n", __func__,
ctx_isp->substate_activated);
return rc;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 259e773..4b2db07 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -1493,19 +1493,6 @@
if (i == ctx->num_base)
master_base_idx = ctx->base[0].idx;
- /* Stop the master CIDs first */
- cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
- master_base_idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
-
- /* stop rest of the CIDs */
- for (i = 0; i < ctx->num_base; i++) {
- if (i == master_base_idx)
- continue;
- cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
- ctx->base[i].idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
- }
-
-
/* Stop the master CSID path first */
cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid,
master_base_idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
@@ -1519,6 +1506,18 @@
ctx->base[i].idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
}
+ /* Stop the master CIDs first */
+ cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
+ master_base_idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
+
+ /* stop rest of the CIDs */
+ for (i = 0; i < ctx->num_base; i++) {
+ if (i == master_base_idx)
+ continue;
+ cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
+ ctx->base[i].idx, CAM_CSID_HALT_AT_FRAME_BOUNDARY);
+ }
+
if (cam_cdm_stream_off(ctx->cdm_handle))
pr_err("%s%d: CDM stream off failed %d\n",
__func__, __LINE__, ctx->cdm_handle);
@@ -2884,7 +2883,7 @@
int i, j;
struct cam_iommu_handle cdm_handles;
- pr_info("%s: Enter\n", __func__);
+ CDBG("%s: Enter\n", __func__);
memset(&g_ife_hw_mgr, 0, sizeof(g_ife_hw_mgr));
@@ -3018,7 +3017,7 @@
/* Create Worker for ife_hw_mgr with 10 tasks */
rc = cam_req_mgr_workq_create("cam_ife_worker", 10,
- &g_ife_hw_mgr.workq);
+ &g_ife_hw_mgr.workq, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
pr_err("%s: Unable to create worker\n", __func__);
@@ -3037,7 +3036,7 @@
hw_mgr_intf->hw_prepare_update = cam_ife_mgr_prepare_hw_update;
hw_mgr_intf->hw_config = cam_ife_mgr_config_hw;
- pr_info("%s: Exit\n", __func__);
+ CDBG("%s: Exit\n", __func__);
return 0;
end:
if (rc) {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 6306df3..3ec9aa6 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -1133,7 +1133,7 @@
if (rc)
return rc;
- /**
+ /*
* configure the IPP and enable the time stamp capture.
* enable the HW measrurement blocks
*/
@@ -1417,7 +1417,7 @@
if (rc)
return rc;
- /**
+ /*
* RDI path config and enable the time stamp capture
* Enable the measurement blocks
*/
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index e62c101..ed251eb 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -1092,10 +1092,9 @@
slot = &in_q->slot[in_q->wr_idx];
if (slot->status != CRM_SLOT_STATUS_NO_REQ &&
- slot->status != CRM_SLOT_STATUS_REQ_APPLIED) {
- CRM_ERR("in_q overwrite %d", slot->status);
- /* @TODO: error handling */
- }
+ slot->status != CRM_SLOT_STATUS_REQ_APPLIED)
+ CRM_WARN("in_q overwrite %d", slot->status);
+
CRM_DBG("sched_req %lld at slot %d",
sched_req->req_id, in_q->wr_idx);
@@ -1106,7 +1105,6 @@
__cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots);
mutex_unlock(&link->req.lock);
- complete(&link->workq_comp);
end:
return rc;
}
@@ -1371,6 +1369,7 @@
goto end;
}
+ CRM_DBG("E: dev %x dev req %lld", add_req->dev_hdl, add_req->req_id);
link = (struct cam_req_mgr_core_link *)
cam_get_device_priv(add_req->link_hdl);
@@ -1404,6 +1403,7 @@
dev_req->dev_hdl = add_req->dev_hdl;
task->process_cb = &cam_req_mgr_process_add_req;
rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
+ CRM_DBG("X: dev %x dev req %lld", add_req->dev_hdl, add_req->req_id);
end:
return rc;
@@ -1813,7 +1813,8 @@
/* Create worker for current link */
snprintf(buf, sizeof(buf), "%x-%x",
link_info->session_hdl, link->link_hdl);
- rc = cam_req_mgr_workq_create(buf, CRM_WORKQ_NUM_TASKS, &link->workq);
+ rc = cam_req_mgr_workq_create(buf, CRM_WORKQ_NUM_TASKS,
+ &link->workq, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
CRM_ERR("FATAL: unable to create worker");
__cam_req_mgr_destroy_link_info(link);
@@ -1919,11 +1920,10 @@
struct cam_req_mgr_sched_request *sched_req)
{
int rc = 0;
- struct crm_workq_task *task = NULL;
struct cam_req_mgr_core_link *link = NULL;
struct cam_req_mgr_core_session *session = NULL;
struct cam_req_mgr_sched_request *sched;
- struct crm_task_payload *task_data;
+ struct crm_task_payload task_data;
if (!sched_req) {
CRM_ERR("csl_req is NULL");
@@ -1942,14 +1942,10 @@
CRM_WARN("session ptr NULL %x", sched_req->link_hdl);
return -EINVAL;
}
+ CRM_DBG("link %x req %lld", sched_req->link_hdl, sched_req->req_id);
- task = cam_req_mgr_workq_get_task(link->workq);
- if (!task)
- return -ENOMEM;
-
- task_data = (struct crm_task_payload *)task->payload;
- task_data->type = CRM_WORKQ_TASK_SCHED_REQ;
- sched = (struct cam_req_mgr_sched_request *)&task_data->u;
+ task_data.type = CRM_WORKQ_TASK_SCHED_REQ;
+ sched = (struct cam_req_mgr_sched_request *)&task_data.u;
sched->req_id = sched_req->req_id;
sched->link_hdl = sched_req->link_hdl;
if (session->force_err_recovery == AUTO_RECOVERY) {
@@ -1958,14 +1954,10 @@
sched->bubble_enable =
(session->force_err_recovery == FORCE_ENABLE_RECOVERY) ? 1 : 0;
}
- task->process_cb = &cam_req_mgr_process_sched_req;
- rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
- /* Blocking call */
- init_completion(&link->workq_comp);
- rc = wait_for_completion_timeout(
- &link->workq_comp,
- msecs_to_jiffies(CAM_REQ_MGR_SCHED_REQ_TIMEOUT));
+ rc = cam_req_mgr_process_sched_req(link, &task_data);
+
+ CRM_DBG("DONE dev %x req %lld", sched_req->link_hdl, sched_req->req_id);
end:
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index 889ee9c..3ee0e2f 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -27,7 +27,7 @@
#define FORCE_ENABLE_RECOVERY 1
#define AUTO_RECOVERY 0
-#define CRM_WORKQ_NUM_TASKS 30
+#define CRM_WORKQ_NUM_TASKS 60
/**
* enum crm_workq_task_type
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
index f53e41c..38dcb42 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
@@ -12,16 +12,30 @@
#include "cam_req_mgr_workq.h"
+#define WORKQ_ACQUIRE_LOCK(workq, flags) {\
+ if ((workq)->in_irq) \
+ spin_lock_irqsave(&(workq)->lock_bh, (flags)); \
+ else \
+ spin_lock_bh(&(workq)->lock_bh); \
+}
+
+#define WORKQ_RELEASE_LOCK(workq, flags) {\
+ if ((workq)->in_irq) \
+ spin_unlock_irqrestore(&(workq)->lock_bh, (flags)); \
+ else \
+ spin_unlock_bh(&(workq)->lock_bh); \
+}
struct crm_workq_task *cam_req_mgr_workq_get_task(
struct cam_req_mgr_core_workq *workq)
{
struct crm_workq_task *task = NULL;
+ unsigned long flags = 0;
if (!workq)
return NULL;
- spin_lock_bh(&workq->lock_bh);
+ WORKQ_ACQUIRE_LOCK(workq, flags);
if (list_empty(&workq->task.empty_head))
goto end;
@@ -33,7 +47,8 @@
}
end:
- spin_unlock_bh(&workq->lock_bh);
+ WORKQ_RELEASE_LOCK(workq, flags);
+
return task;
}
@@ -41,8 +56,9 @@
{
struct cam_req_mgr_core_workq *workq =
(struct cam_req_mgr_core_workq *)task->parent;
+ unsigned long flags = 0;
- spin_lock_bh(&workq->lock_bh);
+ WORKQ_ACQUIRE_LOCK(workq, flags);
list_del_init(&task->entry);
task->cancel = 0;
task->process_cb = NULL;
@@ -50,7 +66,7 @@
list_add_tail(&task->entry,
&workq->task.empty_head);
atomic_add(1, &workq->task.free_cnt);
- spin_unlock_bh(&workq->lock_bh);
+ WORKQ_RELEASE_LOCK(workq, flags);
}
/**
@@ -131,6 +147,7 @@
{
int rc = 0;
struct cam_req_mgr_core_workq *workq = NULL;
+ unsigned long flags = 0;
if (!task) {
CRM_WARN("NULL task pointer can not schedule");
@@ -148,24 +165,25 @@
goto end;
}
- spin_lock_bh(&workq->lock_bh);
if (task->cancel == 1) {
cam_req_mgr_workq_put_task(task);
CRM_WARN("task aborted and queued back to pool");
rc = 0;
- spin_unlock_bh(&workq->lock_bh);
goto end;
}
task->priv = priv;
task->priority =
(prio < CRM_TASK_PRIORITY_MAX && prio >= CRM_TASK_PRIORITY_0)
? prio : CRM_TASK_PRIORITY_0;
+
+ WORKQ_ACQUIRE_LOCK(workq, flags);
list_add_tail(&task->entry,
&workq->task.process_head[task->priority]);
+ WORKQ_RELEASE_LOCK(workq, flags);
+
atomic_add(1, &workq->task.pending_cnt);
CRM_DBG("enq task %pK pending_cnt %d",
task, atomic_read(&workq->task.pending_cnt));
- spin_unlock_bh(&workq->lock_bh);
queue_work(workq->job, &workq->work);
@@ -174,7 +192,7 @@
}
int cam_req_mgr_workq_create(char *name, int32_t num_tasks,
- struct cam_req_mgr_core_workq **workq)
+ struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq)
{
int32_t i;
struct crm_workq_task *task;
@@ -209,6 +227,7 @@
for (i = CRM_TASK_PRIORITY_0; i < CRM_TASK_PRIORITY_MAX; i++)
INIT_LIST_HEAD(&crm_workq->task.process_head[i]);
INIT_LIST_HEAD(&crm_workq->task.empty_head);
+ crm_workq->in_irq = in_irq;
crm_workq->task.num_task = num_tasks;
crm_workq->task.pool = (struct crm_workq_task *)
kzalloc(sizeof(struct crm_workq_task) *
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
index 7d8ca59..eb3b804 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
@@ -25,9 +25,16 @@
/* Task priorities, lower the number higher the priority*/
enum crm_task_priority {
- CRM_TASK_PRIORITY_0 = 0,
- CRM_TASK_PRIORITY_1 = 1,
- CRM_TASK_PRIORITY_MAX = 2,
+ CRM_TASK_PRIORITY_0,
+ CRM_TASK_PRIORITY_1,
+ CRM_TASK_PRIORITY_MAX,
+};
+
+/* workqueue will be used from irq context or not */
+enum crm_workq_context {
+ CRM_WORKQ_USAGE_NON_IRQ,
+ CRM_WORKQ_USAGE_IRQ,
+ CRM_WORKQ_USAGE_INVALID,
};
/** struct crm_workq_task
@@ -58,8 +65,9 @@
* @work : work token used by workqueue
* @job : workqueue internal job struct
* task -
- * @lock : lock for task structs
- * @free_cnt : num of free/available tasks
+ * @lock_bh : lock for task structs
+ * @in_irq : set true if workque can be used in irq context
+ * @free_cnt : num of free/available tasks
* @empty_head : list head of available taska which can be used
* or acquired in order to enqueue a task to workq
* @pool : pool of tasks used for handling events in workq context
@@ -70,6 +78,7 @@
struct work_struct work;
struct workqueue_struct *job;
spinlock_t lock_bh;
+ uint32_t in_irq;
/* tasks */
struct {
@@ -91,11 +100,12 @@
* of session handle and link handle
* @num_task : Num_tasks to be allocated for workq
* @workq : Double pointer worker
+ * @in_irq : Set to one if workq might be used in irq context
* This function will allocate and create workqueue and pass
* the workq pointer to caller.
*/
int cam_req_mgr_workq_create(char *name, int32_t num_tasks,
- struct cam_req_mgr_core_workq **workq);
+ struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq);
/**
* cam_req_mgr_workq_destroy()
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 2fa39c8..140542b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1904,14 +1904,14 @@
}
rc = cam_req_mgr_workq_create("icp_command_queue", ICP_WORKQ_NUM_TASK,
- &icp_hw_mgr.cmd_work);
+ &icp_hw_mgr.cmd_work, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
pr_err("unable to create a worker\n");
goto cmd_work_failed;
}
rc = cam_req_mgr_workq_create("icp_message_queue", ICP_WORKQ_NUM_TASK,
- &icp_hw_mgr.msg_work);
+ &icp_hw_mgr.msg_work, CRM_WORKQ_USAGE_IRQ);
if (rc < 0) {
pr_err("unable to create a worker\n");
goto msg_work_failed;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index 34243e6..15b8a2d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -227,9 +227,10 @@
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
u32 ot_lim;
- u32 reg_off_vbif_lim_conf = (params->xin_id / 4) * 4 +
- params->reg_off_vbif_lim_conf;
- u32 bit_off_vbif_lim_conf = (params->xin_id % 4) * 8;
+ u32 reg_off_vbif_lim_conf = ((params->xin_id / mdata->npriority_lvl)
+ * mdata->npriority_lvl)
+ + params->reg_off_vbif_lim_conf;
+ u32 bit_off_vbif_lim_conf = (params->xin_id % mdata->npriority_lvl) * 8;
u32 reg_val;
u32 sts;
bool forced_on;
@@ -420,6 +421,136 @@
}
}
+static void sde_mdp_parse_cdp_setting(struct platform_device *pdev,
+ struct sde_rot_data_type *mdata)
+{
+ int rc;
+ u32 len, data[SDE_ROT_OP_MAX] = {0};
+
+ len = sde_mdp_parse_dt_prop_len(pdev,
+ "qcom,mdss-rot-cdp-setting");
+ if (len == SDE_ROT_OP_MAX) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-rot-cdp-setting", data, len);
+ if (rc) {
+ SDEROT_ERR("invalid CDP setting\n");
+ goto end;
+ }
+
+ set_bit(SDE_QOS_CDP, mdata->sde_qos_map);
+ mdata->enable_cdp[SDE_ROT_RD] = data[SDE_ROT_RD];
+ mdata->enable_cdp[SDE_ROT_WR] = data[SDE_ROT_WR];
+ return;
+ }
+end:
+ clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
+}
+
+static void sde_mdp_parse_rot_lut_setting(struct platform_device *pdev,
+ struct sde_rot_data_type *mdata)
+{
+ int rc;
+ u32 len, data[4];
+
+ len = sde_mdp_parse_dt_prop_len(pdev, "qcom,mdss-rot-qos-lut");
+ if (len == 4) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-rot-qos-lut", data, len);
+ if (!rc) {
+ mdata->lut_cfg[SDE_ROT_RD].creq_lut_0 = data[0];
+ mdata->lut_cfg[SDE_ROT_RD].creq_lut_1 = data[1];
+ mdata->lut_cfg[SDE_ROT_WR].creq_lut_0 = data[2];
+ mdata->lut_cfg[SDE_ROT_WR].creq_lut_1 = data[3];
+ set_bit(SDE_QOS_LUT, mdata->sde_qos_map);
+ } else {
+ SDEROT_DBG("qos lut setting not found\n");
+ }
+ }
+
+ len = sde_mdp_parse_dt_prop_len(pdev, "qcom,mdss-rot-danger-lut");
+ if (len == SDE_ROT_OP_MAX) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-rot-danger-lut", data, len);
+ if (!rc) {
+ mdata->lut_cfg[SDE_ROT_RD].danger_lut
+ = data[SDE_ROT_RD];
+ mdata->lut_cfg[SDE_ROT_WR].danger_lut
+ = data[SDE_ROT_WR];
+ set_bit(SDE_QOS_DANGER_LUT, mdata->sde_qos_map);
+ } else {
+ SDEROT_DBG("danger lut setting not found\n");
+ }
+ }
+
+ len = sde_mdp_parse_dt_prop_len(pdev, "qcom,mdss-rot-safe-lut");
+ if (len == SDE_ROT_OP_MAX) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-rot-safe-lut", data, len);
+ if (!rc) {
+ mdata->lut_cfg[SDE_ROT_RD].safe_lut = data[SDE_ROT_RD];
+ mdata->lut_cfg[SDE_ROT_WR].safe_lut = data[SDE_ROT_WR];
+ set_bit(SDE_QOS_SAFE_LUT, mdata->sde_qos_map);
+ } else {
+ SDEROT_DBG("safe lut setting not found\n");
+ }
+ }
+}
+
+static void sde_mdp_parse_inline_rot_lut_setting(struct platform_device *pdev,
+ struct sde_rot_data_type *mdata)
+{
+ int rc;
+ u32 len, data[4];
+
+ len = sde_mdp_parse_dt_prop_len(pdev, "qcom,mdss-inline-rot-qos-lut");
+ if (len == 4) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-inline-rot-qos-lut", data, len);
+ if (!rc) {
+ mdata->inline_lut_cfg[SDE_ROT_RD].creq_lut_0 = data[0];
+ mdata->inline_lut_cfg[SDE_ROT_RD].creq_lut_1 = data[1];
+ mdata->inline_lut_cfg[SDE_ROT_WR].creq_lut_0 = data[2];
+ mdata->inline_lut_cfg[SDE_ROT_WR].creq_lut_1 = data[3];
+ set_bit(SDE_INLINE_QOS_LUT, mdata->sde_inline_qos_map);
+ } else {
+ SDEROT_DBG("inline qos lut setting not found\n");
+ }
+ }
+
+ len = sde_mdp_parse_dt_prop_len(pdev,
+ "qcom,mdss-inline-rot-danger-lut");
+ if (len == SDE_ROT_OP_MAX) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-inline-rot-danger-lut", data, len);
+ if (!rc) {
+ mdata->inline_lut_cfg[SDE_ROT_RD].danger_lut
+ = data[SDE_ROT_RD];
+ mdata->inline_lut_cfg[SDE_ROT_WR].danger_lut
+ = data[SDE_ROT_WR];
+ set_bit(SDE_INLINE_QOS_DANGER_LUT,
+ mdata->sde_inline_qos_map);
+ } else {
+ SDEROT_DBG("inline danger lut setting not found\n");
+ }
+ }
+
+ len = sde_mdp_parse_dt_prop_len(pdev, "qcom,mdss-inline-rot-safe-lut");
+ if (len == SDE_ROT_OP_MAX) {
+ rc = sde_mdp_parse_dt_handler(pdev,
+ "qcom,mdss-inline-rot-safe-lut", data, len);
+ if (!rc) {
+ mdata->inline_lut_cfg[SDE_ROT_RD].safe_lut
+ = data[SDE_ROT_RD];
+ mdata->inline_lut_cfg[SDE_ROT_WR].safe_lut
+ = data[SDE_ROT_WR];
+ set_bit(SDE_INLINE_QOS_SAFE_LUT,
+ mdata->sde_inline_qos_map);
+ } else {
+ SDEROT_DBG("inline safe lut setting not found\n");
+ }
+ }
+}
+
static int sde_mdp_parse_dt_misc(struct platform_device *pdev,
struct sde_rot_data_type *mdata)
{
@@ -444,8 +575,14 @@
SDEROT_DBG(
"Could not read optional property: highest bank bit\n");
+ sde_mdp_parse_cdp_setting(pdev, mdata);
+
sde_mdp_parse_vbif_qos(pdev, mdata);
+ sde_mdp_parse_rot_lut_setting(pdev, mdata);
+
+ sde_mdp_parse_inline_rot_lut_setting(pdev, mdata);
+
mdata->mdp_base = mdata->sde_io.base + SDE_MDP_OFFSET;
return 0;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index 9194b44..313c709 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -40,6 +40,9 @@
#define SDE_MDP_HW_REV_301 SDE_MDP_REV(3, 0, 1) /* 8998 v1.1 */
#define SDE_MDP_HW_REV_400 SDE_MDP_REV(4, 0, 0) /* sdm845 v1.0 */
+#define SDE_MDP_VBIF_4_LEVEL_REMAPPER 4
+#define SDE_MDP_VBIF_8_LEVEL_REMAPPER 8
+
struct sde_mult_factor {
uint32_t numer;
uint32_t denom;
@@ -77,9 +80,19 @@
SDE_QOS_PER_PIPE_LUT,
SDE_QOS_SIMPLIFIED_PREFILL,
SDE_QOS_VBLANK_PANIC_CTRL,
+ SDE_QOS_LUT,
+ SDE_QOS_DANGER_LUT,
+ SDE_QOS_SAFE_LUT,
SDE_QOS_MAX,
};
+enum sde_inline_qos_settings {
+ SDE_INLINE_QOS_LUT,
+ SDE_INLINE_QOS_DANGER_LUT,
+ SDE_INLINE_QOS_SAFE_LUT,
+ SDE_INLINE_QOS_MAX,
+};
+
/**
* enum sde_rot_type: SDE rotator HW version
* @SDE_ROT_TYPE_V1_0: V1.0 HW version
@@ -98,6 +111,7 @@
* @SDE_CAPS_R3_1P5_DOWNSCALE: 1.5x downscale rotator support
* @SDE_CAPS_SBUF_1: stream buffer support for inline rotation
* @SDE_CAPS_UBWC_2: universal bandwidth compression version 2
+ * @SDE_CAPS_PARTIALWR: partial write override
*/
enum sde_caps_settings {
SDE_CAPS_R1_WB,
@@ -106,6 +120,7 @@
SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
SDE_CAPS_SBUF_1,
SDE_CAPS_UBWC_2,
+ SDE_CAPS_PARTIALWR,
SDE_CAPS_MAX,
};
@@ -115,6 +130,12 @@
SDE_MAX_BUS_CLIENTS
};
+enum sde_rot_op {
+ SDE_ROT_RD,
+ SDE_ROT_WR,
+ SDE_ROT_OP_MAX
+};
+
enum sde_rot_regdump_access {
SDE_ROT_REGDUMP_READ,
SDE_ROT_REGDUMP_WRITE,
@@ -165,6 +186,13 @@
enum sde_rot_regdump_access access;
};
+struct sde_rot_lut_cfg {
+ u32 creq_lut_0;
+ u32 creq_lut_1;
+ u32 danger_lut;
+ u32 safe_lut;
+};
+
struct sde_rot_data_type {
u32 mdss_version;
@@ -177,6 +205,7 @@
/* bitmap to track qos applicable settings */
DECLARE_BITMAP(sde_qos_map, SDE_QOS_MAX);
+ DECLARE_BITMAP(sde_inline_qos_map, SDE_QOS_MAX);
/* bitmap to track capability settings */
DECLARE_BITMAP(sde_caps_map, SDE_CAPS_MAX);
@@ -210,6 +239,11 @@
void *sde_rot_hw;
int sec_cam_en;
+ u32 enable_cdp[SDE_ROT_OP_MAX];
+
+ struct sde_rot_lut_cfg lut_cfg[SDE_ROT_OP_MAX];
+ struct sde_rot_lut_cfg inline_lut_cfg[SDE_ROT_OP_MAX];
+
struct ion_client *iclient;
bool clk_always_on;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 30fda07..44a29aa 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -60,6 +60,9 @@
/* waiting for hw time out, 3 vsync for 30fps*/
#define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
+/* waiting for inline hw start */
+#define ROT_INLINE_START_TIMEOUT_IN_MS 2000
+
/* default pixel per clock ratio */
#define ROT_PIXEL_PER_CLK_NUMERATOR 36
#define ROT_PIXEL_PER_CLK_DENOMINATOR 10
@@ -299,13 +302,13 @@
return 0;
}
-static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
+static int sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
{
int ret;
- if (WARN_ON(mgr->regulator_enable == on)) {
+ if (mgr->regulator_enable == on) {
SDEROT_ERR("Regulators already in selected mode on=%d\n", on);
- return;
+ return 0;
}
SDEROT_EVTLOG(on);
@@ -327,9 +330,9 @@
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
- SDEROT_WARN("Rotator regulator failed to %s\n",
- on ? "enable" : "disable");
- return;
+ pr_err("rotator regulator failed to %s ret:%d client:%d\n",
+ on ? "enable" : "disable", ret, mgr->rsc_client != NULL);
+ return ret;
}
if (mgr->ops_hw_post_pmevent)
@@ -341,6 +344,7 @@
}
mgr->regulator_enable = on;
+ return 0;
}
static int sde_rotator_enable_clk(struct sde_rot_mgr *mgr, int clk_idx)
@@ -1508,6 +1512,8 @@
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_FLUSH] = ktime_get();
+ SDEROT_EVTLOG(entry->item.session_id, 1);
+
queue_work(entry->doneq->rot_work_queue, &entry->done_work);
sde_rot_mgr_unlock(mgr);
return;
@@ -1564,6 +1570,13 @@
entry->item.flags,
entry->dnsc_factor_w, entry->dnsc_factor_h);
+ wait_for_completion_timeout(
+ &entry->item.inline_start,
+ msecs_to_jiffies(ROT_INLINE_START_TIMEOUT_IN_MS));
+
+ if (entry->item.ts)
+ entry->item.ts[SDE_ROTATOR_TS_START] = ktime_get();
+
SDEROT_EVTLOG(entry->item.session_id, 0);
ret = mgr->ops_wait_for_entry(hw, entry);
if (ret) {
@@ -2332,11 +2345,36 @@
for (i = 0; i < count; i++) {
req->entries[i].item = items[i];
req->entries[i].private = private;
+
+ init_completion(&req->entries[i].item.inline_start);
+ complete_all(&req->entries[i].item.inline_start);
}
return req;
}
+void sde_rotator_req_reset_start(struct sde_rot_entry_container *req)
+{
+ int i;
+
+ if (!req)
+ return;
+
+ for (i = 0; i < req->count; i++)
+ reinit_completion(&req->entries[i].item.inline_start);
+}
+
+void sde_rotator_req_set_start(struct sde_rot_entry_container *req)
+{
+ int i;
+
+ if (!req)
+ return;
+
+ for (i = 0; i < req->count; i++)
+ complete_all(&req->entries[i].item.inline_start);
+}
+
void sde_rotator_req_finish(struct sde_rot_mgr *mgr,
struct sde_rot_file_private *private,
struct sde_rot_entry_container *req)
@@ -2885,12 +2923,11 @@
}
*pmgr = mgr;
-
- pm_runtime_set_suspended(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- SDEROT_ERR("fail to enable power, force on\n");
- sde_rotator_footswitch_ctrl(mgr, true);
+ ret = sde_rotator_footswitch_ctrl(mgr, true);
+ if (ret) {
+ SDEROT_ERR("res_init failed %d\n", ret);
+ ret = -EPROBE_DEFER;
+ goto error_fs_en_fail;
}
/* enable power and clock before h/w initialization/query */
@@ -2931,6 +2968,9 @@
/* disable power and clock after h/w initialization/query */
sde_rotator_clk_ctrl(mgr, false);
sde_rotator_resource_ctrl(mgr, false);
+ sde_rotator_footswitch_ctrl(mgr, false);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
return 0;
@@ -2940,7 +2980,8 @@
error_map_hw_ops:
sde_rotator_clk_ctrl(mgr, false);
sde_rotator_resource_ctrl(mgr, false);
- pm_runtime_disable(mgr->device);
+ sde_rotator_footswitch_ctrl(mgr, false);
+error_fs_en_fail:
sde_rotator_res_destroy(mgr);
error_res_init:
error_parse_dt:
@@ -3024,8 +3065,7 @@
SDEROT_DBG("begin runtime_active\n");
ATRACE_BEGIN("runtime_active");
- sde_rotator_footswitch_ctrl(mgr, true);
- return 0;
+ return sde_rotator_footswitch_ctrl(mgr, true);
}
/*
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 0051e96..7b8a066 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/pm_runtime.h>
+#include <linux/completion.h>
#include "sde_rotator_base.h"
#include "sde_rotator_util.h"
@@ -115,6 +116,7 @@
SDE_ROTATOR_TS_QUEUE, /* wait for h/w resource */
SDE_ROTATOR_TS_COMMIT, /* prepare h/w command */
SDE_ROTATOR_TS_FLUSH, /* initiate h/w processing */
+ SDE_ROTATOR_TS_START, /* h/w triggered (if inline) */
SDE_ROTATOR_TS_DONE, /* receive h/w completion */
SDE_ROTATOR_TS_RETIRE, /* signal destination buffer fence */
SDE_ROTATOR_TS_SRCDQB, /* dequeue source buffer */
@@ -199,6 +201,9 @@
/* Time stamp for profiling purposes */
ktime_t *ts;
+
+ /* Completion structure for inline rotation */
+ struct completion inline_start;
};
/*
@@ -604,6 +609,23 @@
u32 count, u32 flags);
/*
+ * sde_rotator_req_reset_start - reset inline h/w 'start' indicator
+ * For inline rotations, the time of rotation start is not controlled
+ * by the rotator driver. This function resets an internal 'start'
+ * indicator that allows the rotator to delay its rotator
+ * timeout waiting until such time as the inline rotation has
+ * really started.
+ * @req: Pointer to rotation request
+ */
+void sde_rotator_req_reset_start(struct sde_rot_entry_container *req);
+
+/*
+ * sde_rotator_req_set_start - set inline h/w 'start' indicator
+ * @req: Pointer to rotation request
+ */
+void sde_rotator_req_set_start(struct sde_rot_entry_container *req);
+
+/*
* sde_rotator_req_finish - notify manager that client is finished with the
* given request and manager can release the request as required
* @rot_dev: Pointer to rotator device
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index e9ff67c..3e686e9 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -789,7 +789,7 @@
start_time));
seq_printf(s,
- "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld d:%lld sdq:%lld ddq:%lld t:%lld oht:%lld\n",
+ "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld st:%lld d:%lld sdq:%lld ddq:%lld t:%lld oht:%lld\n",
i,
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FENCE],
ts[SDE_ROTATOR_TS_SRCQB])),
@@ -801,8 +801,10 @@
ts[SDE_ROTATOR_TS_QUEUE])),
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FLUSH],
ts[SDE_ROTATOR_TS_COMMIT])),
- ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_DONE],
+ ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_START],
ts[SDE_ROTATOR_TS_FLUSH])),
+ ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_DONE],
+ ts[SDE_ROTATOR_TS_START])),
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_RETIRE],
ts[SDE_ROTATOR_TS_DONE])),
ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_SRCDQB],
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 90b7194..2e91d54 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1467,7 +1467,9 @@
int scid = llcc_get_slice_id(ctx->slice);
/* allocate slot for timestamp */
- ts = stats->ts[stats->count++ % SDE_ROTATOR_NUM_EVENTS];
+ ts = stats->ts[stats->count % SDE_ROTATOR_NUM_EVENTS];
+ if (cmd_type == SDE_ROTATOR_INLINE_CMD_COMMIT)
+ stats->count++;
if (cmd->rot90)
flags |= SDE_ROTATION_90;
@@ -1637,6 +1639,8 @@
goto error_handle_request;
}
+ sde_rotator_req_reset_start(req);
+
sde_rotator_commit_request(rot_dev->mgr, ctx->private, req);
request->committed = true;
@@ -1644,6 +1648,15 @@
/* save request in private handle */
cmd->priv_handle = request;
+ } else if (cmd_type == SDE_ROTATOR_INLINE_CMD_START) {
+ if (!cmd->priv_handle) {
+ ret = -EINVAL;
+ SDEROT_ERR("invalid private handle\n");
+ goto error_invalid_handle;
+ }
+
+ request = cmd->priv_handle;
+ sde_rotator_req_set_start(request->req);
} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_CLEANUP) {
if (!cmd->priv_handle) {
ret = -EINVAL;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h
index 051db78..de448a4 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-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
@@ -65,6 +65,8 @@
#define MMSS_VBIF_NRT_VBIF_IN_WR_LIM_CONF2 0x00C8
#define MMSS_VBIF_NRT_VBIF_OUT_RD_LIM_CONF0 0x00D0
#define MMSS_VBIF_NRT_VBIF_OUT_WR_LIM_CONF0 0x00D4
+#define MMSS_VBIF_NRT_VBIF_QOS_RP_REMAP_000 0x0550
+#define MMSS_VBIF_NRT_VBIF_QOS_LVL_REMAP_000 0x0590
#define SDE_MDP_REG_TRAFFIC_SHAPER_EN BIT(31)
#define SDE_MDP_REG_TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4))
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
index 27fd0c3..705eb27 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
@@ -25,11 +25,13 @@
* enum sde_rotator_inline_cmd_type - inline rotator command stages
* @SDE_ROTATOR_INLINE_CMD_VALIDATE: validate command only
* @SDE_ROTATOR_INLINE_CMD_COMMIT: commit command to hardware
+ * @SDE_ROTATOR_INLINE_CMD_START: ready to start inline rotation
* @SDE_ROTATOR_INLINE_CMD_CLEANUP: cleanup after commit is done
*/
enum sde_rotator_inline_cmd_type {
SDE_ROTATOR_INLINE_CMD_VALIDATE,
SDE_ROTATOR_INLINE_CMD_COMMIT,
+ SDE_ROTATOR_INLINE_CMD_START,
SDE_ROTATOR_INLINE_CMD_CLEANUP,
};
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 980df9f..6ebfc1a 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -493,6 +493,12 @@
SDE_ROT_REGDUMP_VBIF },
};
+struct sde_rot_cdp_params {
+ bool enable;
+ struct sde_mdp_format_params *fmt;
+ u32 offset;
+};
+
/* Invalid software timestamp value for initialization */
#define SDE_REGDMA_SWTS_INVALID (~0)
@@ -741,6 +747,76 @@
}
/*
+ * sde_hw_rotator_vbif_setting - helper function to set vbif QoS remapper
+ * levels, enable write gather enable and avoid clk gating setting for
+ * debug purpose.
+ *
+ * @rot: Pointer to rotator hw
+ */
+static void sde_hw_rotator_vbif_setting(struct sde_hw_rotator *rot)
+{
+ u32 i, mask, vbif_qos, reg_val = 0;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+
+ /* VBIF_ROT QoS remapper setting */
+ switch (mdata->npriority_lvl) {
+
+ case SDE_MDP_VBIF_4_LEVEL_REMAPPER:
+ for (i = 0; i < mdata->npriority_lvl; i++) {
+ reg_val = SDE_VBIF_READ(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4);
+ mask = 0x3 << (XIN_SSPP * 2);
+ vbif_qos = mdata->vbif_nrt_qos[i];
+ reg_val |= vbif_qos << (XIN_SSPP * 2);
+ /* ensure write is issued after the read operation */
+ mb();
+ SDE_VBIF_WRITE(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4,
+ reg_val);
+ }
+ break;
+
+ case SDE_MDP_VBIF_8_LEVEL_REMAPPER:
+ mask = mdata->npriority_lvl - 1;
+ for (i = 0; i < mdata->npriority_lvl; i++) {
+ /* RD and WR client */
+ reg_val |= (mdata->vbif_nrt_qos[i] & mask)
+ << (XIN_SSPP * 4);
+ reg_val |= (mdata->vbif_nrt_qos[i] & mask)
+ << (XIN_WRITEBACK * 4);
+
+ SDE_VBIF_WRITE(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_RP_REMAP_000 + i*8,
+ reg_val);
+ SDE_VBIF_WRITE(mdata,
+ MMSS_VBIF_NRT_VBIF_QOS_LVL_REMAP_000 + i*8,
+ reg_val);
+ }
+ break;
+
+ default:
+ SDEROT_DBG("invalid vbif remapper levels\n");
+ }
+
+ /* Enable write gather for writeback to remove write gaps, which
+ * may hang AXI/BIMC/SDE.
+ */
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
+ BIT(XIN_WRITEBACK));
+
+ /*
+ * For debug purpose, disable clock gating, i.e. Clocks always on
+ */
+ if (mdata->clk_always_on) {
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_CLKON, 0x3);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0, 0x3);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL1,
+ 0xFFFF);
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_CLK_CTRL, 1);
+ }
+}
+
+/*
* sde_hw_rotator_setup_timestamp_packet - setup timestamp writeback command
* @ctx: Pointer to rotator context
* @mask: Bit mask location of the timestamp
@@ -796,6 +872,156 @@
}
/*
+ * sde_hw_rotator_cdp_configs - configures the CDP registers
+ * @ctx: Pointer to rotator context
+ * @params: Pointer to parameters needed for CDP configs
+ */
+static void sde_hw_rotator_cdp_configs(struct sde_hw_rotator_context *ctx,
+ struct sde_rot_cdp_params *params)
+{
+ int reg_val;
+ u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ if (!params->enable) {
+ SDE_REGDMA_WRITE(wrptr, params->offset, 0x0);
+ goto end;
+ }
+
+ reg_val = BIT(0); /* enable cdp */
+
+ if (sde_mdp_is_ubwc_format(params->fmt))
+ reg_val |= BIT(1); /* enable UBWC meta cdp */
+
+ if (sde_mdp_is_ubwc_format(params->fmt)
+ || sde_mdp_is_tilea4x_format(params->fmt)
+ || sde_mdp_is_tilea5x_format(params->fmt))
+ reg_val |= BIT(2); /* enable tile amortize */
+
+ reg_val |= BIT(3); /* enable preload addr ahead cnt 64 */
+
+ SDE_REGDMA_WRITE(wrptr, params->offset, reg_val);
+
+end:
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_qos_lut_wr - Set QoS LUT/Danger LUT/Safe LUT configs
+ * for the WRITEBACK rotator for inline and offline rotation.
+ *
+ * @ctx: Pointer to rotator context
+ */
+static void sde_hw_rotator_setup_qos_lut_wr(struct sde_hw_rotator_context *ctx)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ /* Offline rotation setting */
+ if (!ctx->sbuf_mode) {
+ /* QOS LUT WR setting */
+ if (test_bit(SDE_QOS_LUT, mdata->sde_qos_map)) {
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_CREQ_LUT_0,
+ mdata->lut_cfg[SDE_ROT_WR].creq_lut_0);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_CREQ_LUT_1,
+ mdata->lut_cfg[SDE_ROT_WR].creq_lut_1);
+ }
+
+ /* Danger LUT WR setting */
+ if (test_bit(SDE_QOS_DANGER_LUT, mdata->sde_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DANGER_LUT,
+ mdata->lut_cfg[SDE_ROT_WR].danger_lut);
+
+ /* Safe LUT WR setting */
+ if (test_bit(SDE_QOS_SAFE_LUT, mdata->sde_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_SAFE_LUT,
+ mdata->lut_cfg[SDE_ROT_WR].safe_lut);
+
+ /* Inline rotation setting */
+ } else {
+ /* QOS LUT WR setting */
+ if (test_bit(SDE_INLINE_QOS_LUT, mdata->sde_inline_qos_map)) {
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_CREQ_LUT_0,
+ mdata->inline_lut_cfg[SDE_ROT_WR].creq_lut_0);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_CREQ_LUT_1,
+ mdata->inline_lut_cfg[SDE_ROT_WR].creq_lut_1);
+ }
+
+ /* Danger LUT WR setting */
+ if (test_bit(SDE_INLINE_QOS_DANGER_LUT,
+ mdata->sde_inline_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DANGER_LUT,
+ mdata->inline_lut_cfg[SDE_ROT_WR].danger_lut);
+
+ /* Safe LUT WR setting */
+ if (test_bit(SDE_INLINE_QOS_SAFE_LUT,
+ mdata->sde_inline_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_SAFE_LUT,
+ mdata->inline_lut_cfg[SDE_ROT_WR].safe_lut);
+ }
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
+ * sde_hw_rotator_setup_qos_lut_rd - Set QoS LUT/Danger LUT/Safe LUT configs
+ * for the SSPP rotator for inline and offline rotation.
+ *
+ * @ctx: Pointer to rotator context
+ */
+static void sde_hw_rotator_setup_qos_lut_rd(struct sde_hw_rotator_context *ctx)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
+ /* Offline rotation setting */
+ if (!ctx->sbuf_mode) {
+ /* QOS LUT RD setting */
+ if (test_bit(SDE_QOS_LUT, mdata->sde_qos_map)) {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_CREQ_LUT_0,
+ mdata->lut_cfg[SDE_ROT_RD].creq_lut_0);
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_CREQ_LUT_1,
+ mdata->lut_cfg[SDE_ROT_RD].creq_lut_1);
+ }
+
+ /* Danger LUT RD setting */
+ if (test_bit(SDE_QOS_DANGER_LUT, mdata->sde_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_DANGER_LUT,
+ mdata->lut_cfg[SDE_ROT_RD].danger_lut);
+
+ /* Safe LUT RD setting */
+ if (test_bit(SDE_QOS_SAFE_LUT, mdata->sde_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SAFE_LUT,
+ mdata->lut_cfg[SDE_ROT_RD].safe_lut);
+
+ /* inline rotation setting */
+ } else {
+ /* QOS LUT RD setting */
+ if (test_bit(SDE_INLINE_QOS_LUT, mdata->sde_inline_qos_map)) {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_CREQ_LUT_0,
+ mdata->inline_lut_cfg[SDE_ROT_RD].creq_lut_0);
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_CREQ_LUT_1,
+ mdata->inline_lut_cfg[SDE_ROT_RD].creq_lut_1);
+ }
+
+ /* Danger LUT RD setting */
+ if (test_bit(SDE_INLINE_QOS_DANGER_LUT,
+ mdata->sde_inline_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_DANGER_LUT,
+ mdata->inline_lut_cfg[SDE_ROT_RD].danger_lut);
+
+ /* Safe LUT RD setting */
+ if (test_bit(SDE_INLINE_QOS_SAFE_LUT,
+ mdata->sde_inline_qos_map))
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SAFE_LUT,
+ mdata->inline_lut_cfg[SDE_ROT_RD].safe_lut);
+ }
+
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+}
+
+/*
* sde_hw_rotator_setup_fetchengine - setup fetch engine
* @ctx: Pointer to rotator context
* @queue_id: Priority queue identifier
@@ -814,6 +1040,7 @@
struct sde_hw_rotator *rot = ctx->rot;
struct sde_mdp_format_params *fmt;
struct sde_mdp_data *data;
+ struct sde_rot_cdp_params cdp_params = {0};
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
u32 *wrptr;
u32 opmode = 0;
@@ -985,13 +1212,29 @@
ctx->is_secure = false;
}
+ /* Update command queue write ptr */
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+ /* CDP register RD setting */
+ cdp_params.enable = test_bit(SDE_QOS_CDP, mdata->sde_qos_map) ?
+ mdata->enable_cdp[SDE_ROT_RD] : false;
+ cdp_params.fmt = fmt;
+ cdp_params.offset = ROT_SSPP_CDP_CNTL;
+ sde_hw_rotator_cdp_configs(ctx, &cdp_params);
+
+ /* QOS LUT/ Danger LUT/ Safe Lut WR setting */
+ sde_hw_rotator_setup_qos_lut_rd(ctx);
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
/*
* Determine if traffic shaping is required. Only enable traffic
* shaping when content is 4k@30fps. The actual traffic shaping
* bandwidth calculation is done in output setup.
*/
- if (((cfg->src_rect->w * cfg->src_rect->h) >= RES_UHD) &&
- (cfg->fps <= 30)) {
+ if (((!ctx->sbuf_mode)
+ && (cfg->src_rect->w * cfg->src_rect->h) >= RES_UHD)
+ && (cfg->fps <= 30)) {
SDEROT_DBG("Enable Traffic Shaper\n");
ctx->is_traffic_shaping = true;
} else {
@@ -1017,9 +1260,11 @@
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
struct sde_mdp_format_params *fmt;
+ struct sde_rot_cdp_params cdp_params = {0};
u32 *wrptr;
u32 pack = 0;
u32 dst_format = 0;
+ u32 partial_write = 0;
int i;
wrptr = sde_hw_rotator_get_regdma_segment(ctx);
@@ -1103,8 +1348,13 @@
cfg->v_downscale_factor |
(cfg->h_downscale_factor << 16));
+ /* partial write check */
+ if (test_bit(SDE_CAPS_PARTIALWR, mdata->sde_caps_map) &&
+ !sde_mdp_is_ubwc_format(fmt))
+ partial_write = BIT(10);
+
/* write config setup for bank configuration */
- SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_WRITE_CONFIG,
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_WRITE_CONFIG, partial_write |
(ctx->rot->highest_bank & 0x3) << 8);
if (test_bit(SDE_CAPS_UBWC_2, mdata->sde_caps_map))
@@ -1120,8 +1370,23 @@
SDE_REGDMA_WRITE(wrptr, ROTTOP_OP_MODE, ctx->op_mode |
(flags & SDE_ROT_FLAG_ROT_90 ? BIT(1) : 0) | BIT(0));
+ sde_hw_rotator_put_regdma_segment(ctx, wrptr);
+
+ /* CDP register WR setting */
+ cdp_params.enable = test_bit(SDE_QOS_CDP, mdata->sde_qos_map) ?
+ mdata->enable_cdp[SDE_ROT_WR] : false;
+ cdp_params.fmt = fmt;
+ cdp_params.offset = ROT_WB_CDP_CNTL;
+ sde_hw_rotator_cdp_configs(ctx, &cdp_params);
+
+ /* QOS LUT/ Danger LUT/ Safe LUT WR setting */
+ sde_hw_rotator_setup_qos_lut_wr(ctx);
+
+ wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+
/* setup traffic shaper for 4k 30fps content or if prefill_bw is set */
- if (ctx->is_traffic_shaping || cfg->prefill_bw) {
+ if (!ctx->sbuf_mode &&
+ (ctx->is_traffic_shaping || cfg->prefill_bw)) {
u32 bw;
/*
@@ -2136,7 +2401,7 @@
item->input.format, item->output.format,
entry->perf->config.frame_rate);
- if (mdata->default_ot_rd_limit) {
+ if (!ctx->sbuf_mode && mdata->default_ot_rd_limit) {
struct sde_mdp_set_ot_params ot_params;
memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
@@ -2158,7 +2423,7 @@
sde_mdp_set_ot_limit(&ot_params);
}
- if (mdata->default_ot_wr_limit) {
+ if (!ctx->sbuf_mode && mdata->default_ot_wr_limit) {
struct sde_mdp_set_ot_params ot_params;
memset(&ot_params, 0, sizeof(struct sde_mdp_set_ot_params));
@@ -2189,46 +2454,9 @@
SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CREQ_LUT, qos_lut);
}
- /* Set CDP control registers to 0 if CDP is disabled */
- if (!test_bit(SDE_QOS_CDP, mdata->sde_qos_map)) {
- SDE_ROTREG_WRITE(rot->mdss_base, ROT_SSPP_CDP_CNTL, 0x0);
- SDE_ROTREG_WRITE(rot->mdss_base, ROT_WB_CDP_CNTL, 0x0);
- }
-
- if (mdata->npriority_lvl > 0) {
- u32 mask, reg_val, i, vbif_qos;
-
- for (i = 0; i < mdata->npriority_lvl; i++) {
- reg_val = SDE_VBIF_READ(mdata,
- MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4);
- mask = 0x3 << (XIN_SSPP * 2);
- reg_val &= ~(mask);
- vbif_qos = mdata->vbif_nrt_qos[i];
- reg_val |= vbif_qos << (XIN_SSPP * 2);
- /* ensure write is issued after the read operation */
- mb();
- SDE_VBIF_WRITE(mdata,
- MMSS_VBIF_NRT_VBIF_QOS_REMAP_00 + i*4,
- reg_val);
- }
- }
-
- /* Enable write gather for writeback to remove write gaps, which
- * may hang AXI/BIMC/SDE.
- */
- SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
- BIT(XIN_WRITEBACK));
-
- /*
- * For debug purpose, disable clock gating, i.e. Clocks always on
- */
- if (mdata->clk_always_on) {
- SDE_VBIF_WRITE(mdata, MMSS_VBIF_CLKON, 0x3);
- SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0, 0x3);
- SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL1,
- 0xFFFF);
- SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_CLK_CTRL, 1);
- }
+ /* VBIF QoS and other settings */
+ if (!ctx->sbuf_mode)
+ sde_hw_rotator_vbif_setting(rot);
return 0;
@@ -2337,7 +2565,6 @@
clear_bit(SDE_QOS_PER_PIPE_IB, mdata->sde_qos_map);
set_bit(SDE_QOS_OVERHEAD_FACTOR, mdata->sde_qos_map);
- clear_bit(SDE_QOS_CDP, mdata->sde_qos_map);
set_bit(SDE_QOS_OTLIM, mdata->sde_qos_map);
set_bit(SDE_QOS_PER_PIPE_LUT, mdata->sde_qos_map);
clear_bit(SDE_QOS_SIMPLIFIED_PREFILL, mdata->sde_qos_map);
@@ -2368,6 +2595,7 @@
SDEROT_DBG("Supporting sys cache inline rotation\n");
set_bit(SDE_CAPS_SBUF_1, mdata->sde_caps_map);
set_bit(SDE_CAPS_UBWC_2, mdata->sde_caps_map);
+ set_bit(SDE_CAPS_PARTIALWR, mdata->sde_caps_map);
rot->inpixfmts = sde_hw_rotator_v4_inpixfmts;
rot->num_inpixfmt = ARRAY_SIZE(sde_hw_rotator_v4_inpixfmts);
rot->outpixfmts = sde_hw_rotator_v4_outpixfmts;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
index aa762dd..d2b81d5 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -81,6 +81,8 @@
#define ROT_SSPP_CREQ_LUT (SDE_ROT_SSPP_OFFSET+0x68)
#define ROT_SSPP_QOS_CTRL (SDE_ROT_SSPP_OFFSET+0x6C)
#define ROT_SSPP_SRC_ADDR_SW_STATUS (SDE_ROT_SSPP_OFFSET+0x70)
+#define ROT_SSPP_CREQ_LUT_0 (SDE_ROT_SSPP_OFFSET+0x74)
+#define ROT_SSPP_CREQ_LUT_1 (SDE_ROT_SSPP_OFFSET+0x78)
#define ROT_SSPP_CURRENT_SRC0_ADDR (SDE_ROT_SSPP_OFFSET+0xA4)
#define ROT_SSPP_CURRENT_SRC1_ADDR (SDE_ROT_SSPP_OFFSET+0xA8)
#define ROT_SSPP_CURRENT_SRC2_ADDR (SDE_ROT_SSPP_OFFSET+0xAC)
@@ -167,6 +169,8 @@
#define ROT_WB_CREQ_LUT (SDE_ROT_WB_OFFSET+0x08C)
#define ROT_WB_QOS_CTRL (SDE_ROT_WB_OFFSET+0x090)
#define ROT_WB_SYS_CACHE_MODE (SDE_ROT_WB_OFFSET+0x094)
+#define ROT_WB_CREQ_LUT_0 (SDE_ROT_WB_OFFSET+0x098)
+#define ROT_WB_CREQ_LUT_1 (SDE_ROT_WB_OFFSET+0x09C)
#define ROT_WB_UBWC_STATIC_CTRL (SDE_ROT_WB_OFFSET+0x144)
#define ROT_WB_SBUF_STATUS_PLANE0 (SDE_ROT_WB_OFFSET+0x148)
#define ROT_WB_SBUF_STATUS_PLANE1 (SDE_ROT_WB_OFFSET+0x14C)
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index a477340..88250e1 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -515,13 +515,13 @@
buffer = HFI_BUFFER_EXTRADATA_OUTPUT2;
break;
case HAL_BUFFER_INTERNAL_SCRATCH:
- buffer = HFI_BUFFER_INTERNAL_SCRATCH;
+ buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH;
break;
case HAL_BUFFER_INTERNAL_SCRATCH_1:
- buffer = HFI_BUFFER_INTERNAL_SCRATCH_1;
+ buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1;
break;
case HAL_BUFFER_INTERNAL_SCRATCH_2:
- buffer = HFI_BUFFER_INTERNAL_SCRATCH_2;
+ buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2;
break;
case HAL_BUFFER_INTERNAL_PERSIST:
buffer = HFI_BUFFER_INTERNAL_PERSIST;
@@ -1523,14 +1523,6 @@
sizeof(struct hfi_vui_timing_info);
break;
}
- case HAL_CONFIG_VPE_DEINTERLACE:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_CONFIG_VPE_DEINTERLACE,
- ((struct hal_enable *)pdata)->enable);
- pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
- break;
- }
case HAL_PARAM_VENC_GENERATE_AUDNAL:
{
create_pkt_enable(pkt->rg_property_data,
@@ -1863,14 +1855,6 @@
pkt->size += sizeof(u32) + sizeof(*work_mode);
break;
}
- case HAL_PARAM_USE_SYS_CACHE:
- {
- create_pkt_enable(pkt->rg_property_data,
- HFI_PROPERTY_PARAM_USE_SYS_CACHE,
- (((struct hal_enable *) pdata)->enable));
- pkt->size += sizeof(u32) * 2;
- break;
- }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index b424fbb..89e8356 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -1082,19 +1082,19 @@
buffreq->buffer[5].buffer_type =
HAL_BUFFER_EXTRADATA_OUTPUT2;
break;
- case HFI_BUFFER_INTERNAL_SCRATCH:
+ case HFI_BUFFER_COMMON_INTERNAL_SCRATCH:
memcpy(&buffreq->buffer[6], hfi_buf_req,
sizeof(struct hfi_buffer_requirements));
buffreq->buffer[6].buffer_type =
HAL_BUFFER_INTERNAL_SCRATCH;
break;
- case HFI_BUFFER_INTERNAL_SCRATCH_1:
+ case HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1:
memcpy(&buffreq->buffer[7], hfi_buf_req,
sizeof(struct hfi_buffer_requirements));
buffreq->buffer[7].buffer_type =
HAL_BUFFER_INTERNAL_SCRATCH_1;
break;
- case HFI_BUFFER_INTERNAL_SCRATCH_2:
+ case HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2:
memcpy(&buffreq->buffer[8], hfi_buf_req,
sizeof(struct hfi_buffer_requirements));
buffreq->buffer[8].buffer_type =
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index 3d3d567..074ea4fa 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -94,10 +94,17 @@
trace_msm_smem_buffer_iommu_op_start("MAP", 0, 0,
align, *iova, *buffer_size);
- /* Map a scatterlist into an SMMU with system cacheability */
- rc = msm_dma_map_sg_attrs(cb->dev, table->sgl,
- table->nents, DMA_BIDIRECTIONAL,
- buf, DMA_ATTR_IOMMU_USE_UPSTREAM_HINT);
+ /* Map a scatterlist into SMMU */
+ if (smem_client->res->sys_cache_present) {
+ /* with sys cache attribute & delayed unmap */
+ rc = msm_dma_map_sg_attrs(cb->dev, table->sgl,
+ table->nents, DMA_BIDIRECTIONAL,
+ buf, DMA_ATTR_IOMMU_USE_UPSTREAM_HINT);
+ } else {
+ /* with delayed unmap */
+ rc = msm_dma_map_sg_lazy(cb->dev, table->sgl,
+ table->nents, DMA_BIDIRECTIONAL, buf);
+ }
if (rc != table->nents) {
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 14eb3ab..aa5f18d 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1202,6 +1202,16 @@
else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES)
num_b = ctrl->val;
+ if ((num_b < inst->capability.bframe.min) ||
+ (num_b > inst->capability.bframe.max)) {
+ dprintk(VIDC_ERR,
+ "Error setting num b frames %d min, max supported is %d, %d\n",
+ num_b, inst->capability.bframe.min,
+ inst->capability.bframe.max);
+ rc = -ENOTSUPP;
+ break;
+ }
+
property_id = HAL_CONFIG_VENC_INTRA_PERIOD;
intra_period.pframes = num_p;
intra_period.bframes = num_b;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 05d6d63..6253632 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -168,6 +168,15 @@
case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT:
msm_vidc_ctrl_get_range(ctrl, &inst->capability.blur_height);
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES:
+ msm_vidc_ctrl_get_range(ctrl, &inst->capability.bframe);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ msm_vidc_ctrl_get_range(ctrl, &inst->capability.slice_mbs);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ msm_vidc_ctrl_get_range(ctrl, &inst->capability.slice_bytes);
+ break;
default:
rc = -EINVAL;
}
@@ -1447,8 +1456,6 @@
}
}
- msm_comm_set_use_sys_cache(inst);
-
/*
* For seq_changed_insufficient, driver should set session_continue
* to firmware after the following sequence
@@ -1934,6 +1941,7 @@
ctrl->val = bufreq->buffer_count_min_host;
break;
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ msm_comm_try_get_bufreqs(inst);
bufreq = get_buff_req_buffer(inst, HAL_BUFFER_INPUT);
if (!bufreq) {
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 058af0e..b1a8e8b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -5636,40 +5636,3 @@
return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height);
}
-void msm_comm_set_use_sys_cache(struct msm_vidc_inst *inst)
-{
- struct hal_enable syscache_use;
- int rc = 0;
-
-
- if (!inst->core->resources.sys_cache_enabled)
- goto exit;
-
- syscache_use.enable = false;
- inst->clk_data.use_sys_cache = false;
-
- if (inst->flags & VIDC_REALTIME)
- syscache_use.enable = true;
-
- if (inst->flags & VIDC_THUMBNAIL)
- syscache_use.enable = false;
-
- dprintk(VIDC_DBG,
- "set_use_sys_cache: enable = %d inst = %pK flags =%d\n",
- syscache_use.enable, inst, inst->flags);
- rc = msm_comm_try_set_prop(inst, HAL_PARAM_USE_SYS_CACHE,
- &syscache_use);
- if (rc) {
- dprintk(VIDC_ERR, "set_use_sys_cache: failed!!\n");
- inst->clk_data.use_sys_cache = false;
- goto exit;
- }
-
- inst->clk_data.use_sys_cache = syscache_use.enable;
-
- return;
-
-exit:
- return;
-}
-
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index f62c132..22491f6 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -31,6 +31,7 @@
bool msm_vidc_thermal_mitigation_disabled = !true;
bool msm_vidc_clock_scaling = true;
bool msm_vidc_debug_timeout = !true;
+bool msm_vidc_syscache_disable = true;
#define MAX_DBG_BUF_SIZE 4096
@@ -185,7 +186,9 @@
__debugfs_create(bool, "clock_scaling",
&msm_vidc_clock_scaling) &&
__debugfs_create(bool, "debug_timeout",
- &msm_vidc_debug_timeout);
+ &msm_vidc_debug_timeout) &&
+ __debugfs_create(bool, "disable_video_syscache",
+ &msm_vidc_syscache_disable);
#undef __debugfs_create
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index f5c8e5a..c23ff82 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -64,6 +64,7 @@
extern bool msm_vidc_thermal_mitigation_disabled;
extern bool msm_vidc_clock_scaling;
extern bool msm_vidc_debug_timeout;
+extern bool msm_vidc_syscache_disable;
#define VIDC_MSG_PRIO2STRING(__level) ({ \
char *__str; \
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 17c3045..37bccbd 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -224,7 +224,6 @@
u32 core_id;
enum hal_work_mode work_mode;
bool low_latency_mode;
- bool use_sys_cache;
};
struct profile_data {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 5cf4628..d259072 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -275,12 +275,12 @@
"cache-slice-names", c, &vsc->name);
}
- res->sys_cache_enabled = true;
+ res->sys_cache_present = true;
return 0;
err_load_subcache_table_fail:
- res->sys_cache_enabled = false;
+ res->sys_cache_present = false;
subcaches->count = 0;
subcaches->subcache_tbl = NULL;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index d76985e..b07785a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -159,6 +159,7 @@
struct dcvs_table *dcvs_tbl;
uint32_t dcvs_tbl_size;
struct dcvs_limit *dcvs_limit;
+ bool sys_cache_present;
bool sys_cache_enabled;
struct subcache_set subcache_set;
struct reg_set reg_set;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 5a8dd26..8968764 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -120,6 +120,11 @@
return device->state != VENUS_STATE_DEINIT;
}
+static inline bool is_sys_cache_present(struct venus_hfi_device *device)
+{
+ return device->res->sys_cache_present;
+}
+
static void __dump_packet(u8 *packet, enum vidc_msg_prio log_level)
{
u32 c = 0, packet_size = *(u32 *)packet;
@@ -3492,7 +3497,7 @@
goto exit;
}
- if (!device->res->sys_cache_enabled)
+ if (!is_sys_cache_present(device))
goto exit;
venus_hfi_for_each_subcache_reverse(device, sinfo) {
@@ -3519,7 +3524,7 @@
return -EINVAL;
}
- if (!device->res->sys_cache_enabled)
+ if (!is_sys_cache_present(device))
return 0;
venus_hfi_for_each_subcache(device, sinfo) {
@@ -3764,7 +3769,7 @@
struct hfi_resource_subcache_type *sc_res;
struct vidc_resource_hdr rhdr;
- if (!device->res->sys_cache_enabled)
+ if (msm_vidc_syscache_disable || !is_sys_cache_present(device))
return 0;
memset((void *)resource, 0x0, (sizeof(u32) * VIDC_MAX_SUBCACHE_SIZE));
@@ -3812,6 +3817,8 @@
dprintk(VIDC_DBG, "Activated & Set Subcaches to Venus\n");
+ device->res->sys_cache_enabled = true;
+
return 0;
err_fail_set_subacaches:
@@ -3830,7 +3837,7 @@
struct hfi_resource_subcache_type *sc_res;
struct vidc_resource_hdr rhdr;
- if (!device->res->sys_cache_enabled)
+ if (msm_vidc_syscache_disable || !is_sys_cache_present(device))
return 0;
dprintk(VIDC_DBG, "Disabling Subcaches\n");
@@ -3877,6 +3884,8 @@
}
}
+ device->res->sys_cache_enabled = false;
+
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 48a6f17..5601f1b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -56,13 +56,6 @@
#define HFI_ERR_SESSION_START_CODE_NOT_FOUND \
(HFI_OX_BASE + 0x1004)
-#define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1)
-#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
-#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
-#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
-#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5)
-#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6)
-#define HFI_BUFFER_INTERNAL_RECON (HFI_OX_BASE + 0x9)
#define HFI_BUFFER_MODE_DYNAMIC (HFI_OX_BASE + 0x3)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index da18377..a2f076b 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -157,7 +157,6 @@
HAL_CONFIG_VPE_OPERATIONS,
HAL_PARAM_VENC_INTRA_REFRESH,
HAL_PARAM_VENC_MULTI_SLICE_CONTROL,
- HAL_CONFIG_VPE_DEINTERLACE,
HAL_SYS_DEBUG_CONFIG,
HAL_CONFIG_BUFFER_REQUIREMENTS,
HAL_CONFIG_PRIORITY,
@@ -224,7 +223,6 @@
HAL_PARAM_VIDEO_CORES_USAGE,
HAL_PARAM_VIDEO_WORK_MODE,
HAL_PARAM_SECURE,
- HAL_PARAM_USE_SYS_CACHE,
};
enum hal_domain {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index fc638f0..2d4a573 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -155,6 +155,13 @@
#define HFI_BUFFER_OUTPUT2 (HFI_COMMON_BASE + 0x3)
#define HFI_BUFFER_INTERNAL_PERSIST (HFI_COMMON_BASE + 0x4)
#define HFI_BUFFER_INTERNAL_PERSIST_1 (HFI_COMMON_BASE + 0x5)
+#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH (HFI_COMMON_BASE + 0x6)
+#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1 (HFI_COMMON_BASE + 0x7)
+#define HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2 (HFI_COMMON_BASE + 0x8)
+#define HFI_BUFFER_COMMON_INTERNAL_RECON (HFI_COMMON_BASE + 0x9)
+#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_COMMON_BASE + 0xA)
+#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_COMMON_BASE + 0xB)
+#define HFI_BUFFER_EXTRADATA_INPUT (HFI_COMMON_BASE + 0xC)
#define HFI_BITDEPTH_8 (HFI_COMMON_BASE + 0x0)
#define HFI_BITDEPTH_9 (HFI_COMMON_BASE + 0x1)
@@ -220,8 +227,6 @@
(HFI_PROPERTY_PARAM_COMMON_START + 0x010)
#define HFI_PROPERTY_PARAM_SECURE_SESSION \
(HFI_PROPERTY_PARAM_COMMON_START + 0x011)
-#define HFI_PROPERTY_PARAM_USE_SYS_CACHE \
- (HFI_PROPERTY_PARAM_COMMON_START + 0x012)
#define HFI_PROPERTY_PARAM_WORK_MODE \
(HFI_PROPERTY_PARAM_COMMON_START + 0x015)
@@ -344,8 +349,6 @@
(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000)
#define HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE \
(HFI_PROPERTY_CONFIG_COMMON_START + 0x010)
-#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE \
- (HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x001)
#define HFI_PROPERTY_CONFIG_VPE_OPERATIONS \
(HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x002)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 27e7cf6..7c24da5 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -206,6 +206,7 @@
}
s5p_mfc_clock_on();
ret = s5p_mfc_init_hw(dev);
+ s5p_mfc_clock_off();
if (ret)
mfc_err("Failed to reinit FW\n");
}
@@ -663,9 +664,9 @@
break;
}
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
- wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
+ wake_up_ctx(ctx, reason, err);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
s5p_mfc_handle_frame(ctx, reason, err);
@@ -679,15 +680,11 @@
case S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET:
ctx->inst_no = s5p_mfc_hw_call(dev->mfc_ops, get_inst_no, dev);
ctx->state = MFCINST_GOT_INST;
- clear_work_bit(ctx);
- wake_up(&ctx->queue);
goto irq_cleanup_hw;
case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
- clear_work_bit(ctx);
ctx->inst_no = MFC_NO_INSTANCE_SET;
ctx->state = MFCINST_FREE;
- wake_up(&ctx->queue);
goto irq_cleanup_hw;
case S5P_MFC_R2H_CMD_SYS_INIT_RET:
@@ -697,9 +694,9 @@
if (ctx)
clear_work_bit(ctx);
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
- wake_up_dev(dev, reason, err);
clear_bit(0, &dev->hw_lock);
clear_bit(0, &dev->enter_suspend);
+ wake_up_dev(dev, reason, err);
break;
case S5P_MFC_R2H_CMD_INIT_BUFFERS_RET:
@@ -714,9 +711,7 @@
break;
case S5P_MFC_R2H_CMD_DPB_FLUSH_RET:
- clear_work_bit(ctx);
ctx->state = MFCINST_RUNNING;
- wake_up(&ctx->queue);
goto irq_cleanup_hw;
default:
@@ -735,6 +730,8 @@
mfc_err("Failed to unlock hw\n");
s5p_mfc_clock_off();
+ clear_work_bit(ctx);
+ wake_up(&ctx->queue);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
spin_unlock(&dev->irqlock);
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 4f8c7ef..db525cd 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1332,8 +1332,8 @@
}
}
}
- if (ep_in == NULL) {
- dev_dbg(&intf->dev, "inbound and/or endpoint not found");
+ if (!ep_in || !ep_out) {
+ dev_dbg(&intf->dev, "required endpoints not found\n");
return -ENODEV;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index 8263c4b..bf4b3ca 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -674,10 +674,8 @@
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto err_free_card;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_cx231xx_pcm_capture);
@@ -691,10 +689,9 @@
INIT_WORK(&dev->wq_trigger, audio_trigger);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto err_free_card;
+
adev->sndcard = card;
adev->udev = dev->udev;
@@ -704,6 +701,11 @@
hs_config_info[0].interface_info.
audio_index + 1];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
+ err = -ENODEV;
+ goto err_free_card;
+ }
+
adev->end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -713,13 +715,20 @@
"audio EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
-
- if (adev->alt_max_pkt_size == NULL)
- return -ENOMEM;
+ if (!adev->alt_max_pkt_size) {
+ err = -ENOMEM;
+ goto err_free_card;
+ }
for (i = 0; i < adev->num_alt; i++) {
- u16 tmp =
- le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
+ err = -ENODEV;
+ goto err_free_pkt_size;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -729,6 +738,13 @@
}
return 0;
+
+err_free_pkt_size:
+ kfree(adev->alt_max_pkt_size);
+err_free_card:
+ snd_card_free(card);
+
+ return err;
}
static int cx231xx_audio_fini(struct cx231xx *dev)
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 36bc254..be9e333 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1397,6 +1397,9 @@
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress;
dev->video_mode.num_alt = uif->num_altsetting;
@@ -1410,7 +1413,12 @@
return -ENOMEM;
for (i = 0; i < dev->video_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
dev_dbg(dev->dev,
"Alternate setting %i, max size= %i\n", i,
@@ -1427,6 +1435,9 @@
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->vbi_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -1443,8 +1454,12 @@
return -ENOMEM;
for (i = 0; i < dev->vbi_mode.num_alt; i++) {
- u16 tmp =
- le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->vbi_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -1464,6 +1479,9 @@
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
dev->sliced_cc_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].desc.
bEndpointAddress;
@@ -1478,7 +1496,12 @@
return -ENOMEM;
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1)
+ return -ENODEV;
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->sliced_cc_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
@@ -1647,6 +1670,11 @@
}
uif = udev->actconfig->interface[idx];
+ if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
+ retval = -ENODEV;
+ goto err_video_alt;
+ }
+
dev->ts1_mode.end_point_addr =
uif->altsetting[0].endpoint[isoc_pipe].
desc.bEndpointAddress;
@@ -1664,7 +1692,14 @@
}
for (i = 0; i < dev->ts1_mode.num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
+ retval = -ENODEV;
+ goto err_video_alt;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].
endpoint[isoc_pipe].desc.
wMaxPacketSize);
dev->ts1_mode.alt_max_pkt_size[i] =
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 47ce9d5..563f690 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -812,6 +812,9 @@
/* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+ if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1)
+ return -ENODEV;
+
purb = usb_alloc_urb(0, GFP_KERNEL);
if (purb == NULL)
return -ENOMEM;
diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c
index d66f56c..1f7bce6 100644
--- a/drivers/media/usb/dvb-usb/dibusb-mc-common.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c
@@ -12,6 +12,8 @@
#include <linux/kconfig.h>
#include "dibusb.h"
+MODULE_LICENSE("GPL");
+
/* 3000MC/P stuff */
// Config Adjacent channels Perf -cal22
static struct dibx000_agc_config dib3000p_mt2060_agc_config = {
diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c
index 4284f69..475a3c0 100644
--- a/drivers/media/usb/dvb-usb/digitv.c
+++ b/drivers/media/usb/dvb-usb/digitv.c
@@ -33,6 +33,9 @@
wo = (rbuf == NULL || rlen == 0); /* write-only */
+ if (wlen > 4 || rlen > 4)
+ return -EIO;
+
memset(st->sndbuf, 0, 7);
memset(st->rcvbuf, 0, 7);
diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index c3e6734..4a0cc54 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
@@ -205,6 +205,20 @@
switch (num) {
case 2:
+ if (msg[0].len != 1) {
+ warn("i2c rd: len=%d is not 1!\n",
+ msg[0].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
+ if (2 + msg[1].len > sizeof(buf6)) {
+ warn("i2c rd: len=%d is too big!\n",
+ msg[1].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
/* read si2109 register by number */
buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
@@ -220,6 +234,13 @@
case 1:
switch (msg[0].addr) {
case 0x68:
+ if (2 + msg[0].len > sizeof(buf6)) {
+ warn("i2c wr: len=%d is too big!\n",
+ msg[0].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
/* write to si2109 register */
buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
@@ -263,6 +284,13 @@
/* first write first register number */
u8 ibuf[MAX_XFER_SIZE], obuf[3];
+ if (2 + msg[0].len != sizeof(obuf)) {
+ warn("i2c rd: len=%d is not 1!\n",
+ msg[0].len);
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
+
if (2 + msg[1].len > sizeof(ibuf)) {
warn("i2c rd: len=%d is too big!\n",
msg[1].len);
@@ -463,6 +491,12 @@
/* first write first register number */
u8 ibuf[MAX_XFER_SIZE], obuf[3];
+ if (2 + msg[0].len != sizeof(obuf)) {
+ warn("i2c rd: len=%d is not 1!\n",
+ msg[0].len);
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
if (2 + msg[1].len > sizeof(ibuf)) {
warn("i2c rd: len=%d is too big!\n",
msg[1].len);
@@ -697,6 +731,13 @@
msg[0].buf[0] = state->data[1];
break;
default:
+ if (3 + msg[0].len > sizeof(state->data)) {
+ warn("i2c wr: len=%d is too big!\n",
+ msg[0].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
/* always i2c write*/
state->data[0] = 0x08;
state->data[1] = msg[0].addr;
@@ -712,6 +753,19 @@
break;
case 2:
/* always i2c read */
+ if (4 + msg[0].len > sizeof(state->data)) {
+ warn("i2c rd: len=%d is too big!\n",
+ msg[0].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+ if (1 + msg[1].len > sizeof(state->data)) {
+ warn("i2c rd: len=%d is too big!\n",
+ msg[1].len);
+ num = -EOPNOTSUPP;
+ break;
+ }
+
state->data[0] = 0x09;
state->data[1] = msg[0].len;
state->data[2] = msg[1].len;
diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c
index ecc207f..9e0d6a4 100644
--- a/drivers/media/usb/dvb-usb/ttusb2.c
+++ b/drivers/media/usb/dvb-usb/ttusb2.c
@@ -78,6 +78,9 @@
u8 *s, *r = NULL;
int ret = 0;
+ if (4 + rlen > 64)
+ return -EIO;
+
s = kzalloc(wlen+4, GFP_KERNEL);
if (!s)
return -ENOMEM;
@@ -381,6 +384,22 @@
write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
read = msg[i].flags & I2C_M_RD;
+ if (3 + msg[i].len > sizeof(obuf)) {
+ err("i2c wr len=%d too high", msg[i].len);
+ break;
+ }
+ if (write_read) {
+ if (3 + msg[i+1].len > sizeof(ibuf)) {
+ err("i2c rd len=%d too high", msg[i+1].len);
+ break;
+ }
+ } else if (read) {
+ if (3 + msg[i].len > sizeof(ibuf)) {
+ err("i2c rd len=%d too high", msg[i].len);
+ break;
+ }
+ }
+
obuf[0] = (msg[i].addr << 1) | (write_read | read);
if (read)
obuf[1] = 0;
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index 40aaaa9..78542ff 100644
--- a/drivers/media/usb/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
@@ -188,6 +188,9 @@
return -EIO;
}
+ if (alt->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index c8b4eb2..bfdf723 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1506,7 +1506,14 @@
}
for (i = 0; i < usbvision->num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
+ u16 tmp;
+
+ if (uif->altsetting[i].desc.bNumEndpoints < 2) {
+ ret = -ENODEV;
+ goto err_pkt;
+ }
+
+ tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
wMaxPacketSize);
usbvision->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index cc128db..e3735bf 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -604,6 +604,14 @@
ptr = pdest = frm->lpvbits;
if (frm->ulState == ZR364XX_READ_IDLE) {
+ if (purb->actual_length < 128) {
+ /* header incomplete */
+ dev_info(&cam->udev->dev,
+ "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n",
+ __func__, purb->actual_length);
+ return -EINVAL;
+ }
+
frm->ulState = ZR364XX_READ_FRAME;
frm->cur_size = 0;
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index dd99b06..fa4fe02 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -1496,8 +1496,6 @@
if ((rc = cxl_native_register_psl_err_irq(adapter)))
goto err;
- /* Release the context lock as adapter is configured */
- cxl_adapter_context_unlock(adapter);
return 0;
err:
@@ -1596,6 +1594,9 @@
if ((rc = cxl_sysfs_adapter_add(adapter)))
goto err_put1;
+ /* Release the context lock as adapter is configured */
+ cxl_adapter_context_unlock(adapter);
+
return adapter;
err_put1:
@@ -1778,7 +1779,7 @@
{
struct cxl *adapter = pci_get_drvdata(pdev);
struct cxl_afu *afu;
- pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET;
+ pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET, afu_result;
int i;
/* At this point, we could still have an interrupt pending.
@@ -1883,16 +1884,26 @@
for (i = 0; i < adapter->slices; i++) {
afu = adapter->afu[i];
- result = cxl_vphb_error_detected(afu, state);
-
- /* Only continue if everyone agrees on NEED_RESET */
- if (result != PCI_ERS_RESULT_NEED_RESET)
- return result;
+ afu_result = cxl_vphb_error_detected(afu, state);
cxl_context_detach_all(afu);
cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
pci_deconfigure_afu(afu);
+
+ /* Disconnect trumps all, NONE trumps NEED_RESET */
+ if (afu_result == PCI_ERS_RESULT_DISCONNECT)
+ result = PCI_ERS_RESULT_DISCONNECT;
+ else if ((afu_result == PCI_ERS_RESULT_NONE) &&
+ (result == PCI_ERS_RESULT_NEED_RESET))
+ result = PCI_ERS_RESULT_NONE;
}
+
+ /* should take the context lock here */
+ if (cxl_adapter_context_lock(adapter) != 0)
+ dev_warn(&adapter->dev,
+ "Couldn't take context lock with %d active-contexts\n",
+ atomic_read(&adapter->contexts_num));
+
cxl_deconfigure_adapter(adapter);
return result;
@@ -1911,6 +1922,13 @@
if (cxl_configure_adapter(adapter, pdev))
goto err;
+ /*
+ * Unlock context activation for the adapter. Ideally this should be
+ * done in cxl_pci_resume but cxlflash module tries to activate the
+ * master context as part of slot_reset callback.
+ */
+ cxl_adapter_context_unlock(adapter);
+
for (i = 0; i < adapter->slices; i++) {
afu = adapter->afu[i];
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index 127a052..871040e 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -50,7 +50,8 @@
#define UID_STATE_TOTAL_CURR 2
#define UID_STATE_TOTAL_LAST 3
-#define UID_STATE_SIZE 4
+#define UID_STATE_DEAD_TASKS 4
+#define UID_STATE_SIZE 5
struct uid_entry {
uid_t uid;
@@ -215,35 +216,44 @@
return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
}
-static void add_uid_io_curr_stats(struct uid_entry *uid_entry,
- struct task_struct *task)
+static void add_uid_io_stats(struct uid_entry *uid_entry,
+ struct task_struct *task, int slot)
{
- struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
+ struct io_stats *io_slot = &uid_entry->io[slot];
- io_curr->read_bytes += task->ioac.read_bytes;
- io_curr->write_bytes += compute_write_bytes(task);
- io_curr->rchar += task->ioac.rchar;
- io_curr->wchar += task->ioac.wchar;
- io_curr->fsync += task->ioac.syscfs;
+ io_slot->read_bytes += task->ioac.read_bytes;
+ io_slot->write_bytes += compute_write_bytes(task);
+ io_slot->rchar += task->ioac.rchar;
+ io_slot->wchar += task->ioac.wchar;
+ io_slot->fsync += task->ioac.syscfs;
}
-static void clean_uid_io_last_stats(struct uid_entry *uid_entry,
- struct task_struct *task)
+static void compute_uid_io_bucket_stats(struct io_stats *io_bucket,
+ struct io_stats *io_curr,
+ struct io_stats *io_last,
+ struct io_stats *io_dead)
{
- struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
+ io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes -
+ io_last->read_bytes;
+ io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes -
+ io_last->write_bytes;
+ io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar;
+ io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar;
+ io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync;
- io_last->read_bytes -= task->ioac.read_bytes;
- io_last->write_bytes -= compute_write_bytes(task);
- io_last->rchar -= task->ioac.rchar;
- io_last->wchar -= task->ioac.wchar;
- io_last->fsync -= task->ioac.syscfs;
+ io_last->read_bytes = io_curr->read_bytes;
+ io_last->write_bytes = io_curr->write_bytes;
+ io_last->rchar = io_curr->rchar;
+ io_last->wchar = io_curr->wchar;
+ io_last->fsync = io_curr->fsync;
+
+ memset(io_dead, 0, sizeof(struct io_stats));
}
static void update_io_stats_all_locked(void)
{
struct uid_entry *uid_entry;
struct task_struct *task, *temp;
- struct io_stats *io_bucket, *io_curr, *io_last;
struct user_namespace *user_ns = current_user_ns();
unsigned long bkt;
uid_t uid;
@@ -258,70 +268,38 @@
uid_entry = find_or_register_uid(uid);
if (!uid_entry)
continue;
- add_uid_io_curr_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
} while_each_thread(temp, task);
rcu_read_unlock();
hash_for_each(hash_table, bkt, uid_entry, hash) {
- io_bucket = &uid_entry->io[uid_entry->state];
- io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
- io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
-
- io_bucket->read_bytes +=
- io_curr->read_bytes - io_last->read_bytes;
- io_bucket->write_bytes +=
- io_curr->write_bytes - io_last->write_bytes;
- io_bucket->rchar += io_curr->rchar - io_last->rchar;
- io_bucket->wchar += io_curr->wchar - io_last->wchar;
- io_bucket->fsync += io_curr->fsync - io_last->fsync;
-
- io_last->read_bytes = io_curr->read_bytes;
- io_last->write_bytes = io_curr->write_bytes;
- io_last->rchar = io_curr->rchar;
- io_last->wchar = io_curr->wchar;
- io_last->fsync = io_curr->fsync;
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
}
}
-static void update_io_stats_uid_locked(uid_t target_uid)
+static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
{
- struct uid_entry *uid_entry;
struct task_struct *task, *temp;
- struct io_stats *io_bucket, *io_curr, *io_last;
struct user_namespace *user_ns = current_user_ns();
- uid_entry = find_or_register_uid(target_uid);
- if (!uid_entry)
- return;
-
memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
sizeof(struct io_stats));
rcu_read_lock();
do_each_thread(temp, task) {
- if (from_kuid_munged(user_ns, task_uid(task)) != target_uid)
+ if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid)
continue;
- add_uid_io_curr_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
} while_each_thread(temp, task);
rcu_read_unlock();
- io_bucket = &uid_entry->io[uid_entry->state];
- io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR];
- io_last = &uid_entry->io[UID_STATE_TOTAL_LAST];
-
- io_bucket->read_bytes +=
- io_curr->read_bytes - io_last->read_bytes;
- io_bucket->write_bytes +=
- io_curr->write_bytes - io_last->write_bytes;
- io_bucket->rchar += io_curr->rchar - io_last->rchar;
- io_bucket->wchar += io_curr->wchar - io_last->wchar;
- io_bucket->fsync += io_curr->fsync - io_last->fsync;
-
- io_last->read_bytes = io_curr->read_bytes;
- io_last->write_bytes = io_curr->write_bytes;
- io_last->rchar = io_curr->rchar;
- io_last->wchar = io_curr->wchar;
- io_last->fsync = io_curr->fsync;
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
}
static int uid_io_show(struct seq_file *m, void *v)
@@ -406,7 +384,7 @@
return count;
}
- update_io_stats_uid_locked(uid);
+ update_io_stats_uid_locked(uid_entry);
uid_entry->state = state;
@@ -444,8 +422,7 @@
uid_entry->utime += utime;
uid_entry->stime += stime;
- update_io_stats_uid_locked(uid);
- clean_uid_io_last_stats(uid_entry, task);
+ add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
exit:
rt_mutex_unlock(&uid_lock);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 3bde96a..f222f8a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -138,6 +138,74 @@
};
EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
+/*
+ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
+ * are placed at a fixed offset.
+ */
+static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (section)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ oobregion->offset = 40;
+ break;
+ case 128:
+ oobregion->offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ oobregion->length = ecc->total;
+ if (oobregion->offset + oobregion->length > mtd->oobsize)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ecc_offset = 0;
+
+ if (section < 0 || section > 1)
+ return -ERANGE;
+
+ switch (mtd->oobsize) {
+ case 64:
+ ecc_offset = 40;
+ break;
+ case 128:
+ ecc_offset = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (section == 0) {
+ oobregion->offset = 2;
+ oobregion->length = ecc_offset - 2;
+ } else {
+ oobregion->offset = ecc_offset + ecc->total;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ }
+
+ return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
+ .ecc = nand_ooblayout_ecc_lp_hamming,
+ .free = nand_ooblayout_free_lp_hamming,
+};
+
static int check_offs_len(struct mtd_info *mtd,
loff_t ofs, uint64_t len)
{
@@ -4565,7 +4633,7 @@
break;
case 64:
case 128:
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
break;
default:
WARN(1, "No oob scheme defined for oobsize %d\n",
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 5513bfd9..c178cb0d 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1856,6 +1856,15 @@
nand_chip->ecc.priv = NULL;
nand_set_flash_node(nand_chip, dev->of_node);
+ if (!mtd->name) {
+ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "omap2-nand.%d", info->gpmc_cs);
+ if (!mtd->name) {
+ dev_err(&pdev->dev, "Failed to set MTD name\n");
+ return -ENOMEM;
+ }
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(nand_chip->IO_ADDR_R))
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 40a7c4a..af2f091 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -23,6 +23,11 @@
#include <asm/sizes.h>
#include <linux/platform_data/mtd-orion_nand.h>
+struct orion_nand_info {
+ struct nand_chip chip;
+ struct clk *clk;
+};
+
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *nc = mtd_to_nand(mtd);
@@ -75,20 +80,21 @@
static int __init orion_nand_probe(struct platform_device *pdev)
{
+ struct orion_nand_info *info;
struct mtd_info *mtd;
struct nand_chip *nc;
struct orion_nand_data *board;
struct resource *res;
- struct clk *clk;
void __iomem *io_base;
int ret = 0;
u32 val = 0;
- nc = devm_kzalloc(&pdev->dev,
- sizeof(struct nand_chip),
+ info = devm_kzalloc(&pdev->dev,
+ sizeof(struct orion_nand_info),
GFP_KERNEL);
- if (!nc)
+ if (!info)
return -ENOMEM;
+ nc = &info->chip;
mtd = nand_to_mtd(nc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -145,15 +151,13 @@
if (board->dev_ready)
nc->dev_ready = board->dev_ready;
- platform_set_drvdata(pdev, mtd);
+ platform_set_drvdata(pdev, info);
/* Not all platforms can gate the clock, so it is not
an error if the clock does not exists. */
- clk = clk_get(&pdev->dev, NULL);
- if (!IS_ERR(clk)) {
- clk_prepare_enable(clk);
- clk_put(clk);
- }
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(info->clk))
+ clk_prepare_enable(info->clk);
if (nand_scan(mtd, 1)) {
ret = -ENXIO;
@@ -170,26 +174,22 @@
return 0;
no_dev:
- if (!IS_ERR(clk)) {
- clk_disable_unprepare(clk);
- clk_put(clk);
- }
+ if (!IS_ERR(info->clk))
+ clk_disable_unprepare(info->clk);
return ret;
}
static int orion_nand_remove(struct platform_device *pdev)
{
- struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct clk *clk;
+ struct orion_nand_info *info = platform_get_drvdata(pdev);
+ struct nand_chip *chip = &info->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
nand_release(mtd);
- clk = clk_get(&pdev->dev, NULL);
- if (!IS_ERR(clk)) {
- clk_disable_unprepare(clk);
- clk_put(clk);
- }
+ if (!IS_ERR(info->clk))
+ clk_disable_unprepare(info->clk);
return 0;
}
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 8716b8c..6f3c805 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -1077,7 +1077,7 @@
* are "42101001.sb" or "42101002.sb"
*/
sprintf(stir421x_fw_name, "4210%4X.sb",
- self->usbdev->descriptor.bcdDevice);
+ le16_to_cpu(self->usbdev->descriptor.bcdDevice));
ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index e1c338c..f15589c 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -37,6 +37,7 @@
{ USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */
{ USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
{ USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */
+ { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */
{ USB_DEVICE(0x0cf3, 0x7015),
.driver_info = AR9287_USB }, /* Atheros */
@@ -1218,6 +1219,9 @@
u8 bulk_out_ep;
int r;
+ if (iface_desc->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
/* Find bulk out endpoint */
for (r = 1; r >= 0; r--) {
endpoint = &iface_desc->endpoint[r].desc;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d472e13..1afed52 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -26,6 +26,10 @@
module_param(use_msi, bool, 0444);
MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
+static bool ftm_mode;
+module_param(ftm_mode, bool, 0444);
+MODULE_PARM_DESC(ftm_mode, " Set factory test mode, default - false");
+
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int wil6210_pm_notify(struct notifier_block *notify_block,
@@ -36,13 +40,15 @@
static
void wil_set_capabilities(struct wil6210_priv *wil)
{
+ const char *wil_fw_name;
u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
RGF_USER_REVISION_ID_MASK);
bitmap_zero(wil->hw_capabilities, hw_capability_last);
bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
- wil->wil_fw_name = WIL_FW_NAME_DEFAULT;
+ wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
+ WIL_FW_NAME_DEFAULT;
wil->chip_revision = chip_revision;
switch (jtag_id) {
@@ -51,9 +57,11 @@
case REVISION_ID_SPARROW_D0:
wil->hw_name = "Sparrow D0";
wil->hw_version = HW_VER_SPARROW_D0;
- if (wil_fw_verify_file_exists(wil,
- WIL_FW_NAME_SPARROW_PLUS))
- wil->wil_fw_name = WIL_FW_NAME_SPARROW_PLUS;
+ wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_SPARROW_PLUS :
+ WIL_FW_NAME_SPARROW_PLUS;
+
+ if (wil_fw_verify_file_exists(wil, wil_fw_name))
+ wil->wil_fw_name = wil_fw_name;
break;
case REVISION_ID_SPARROW_B0:
wil->hw_name = "Sparrow B0";
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index d05bb36..ba1c33b 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -38,8 +38,13 @@
extern bool disable_ap_sme;
#define WIL_NAME "wil6210"
-#define WIL_FW_NAME_DEFAULT "wil6210.fw" /* code Sparrow B0 */
-#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */
+
+#define WIL_FW_NAME_DEFAULT "wil6210.fw"
+#define WIL_FW_NAME_FTM_DEFAULT "wil6210_ftm.fw"
+
+#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw"
+#define WIL_FW_NAME_FTM_SPARROW_PLUS "wil6210_sparrow_plus_ftm.fw"
+
#define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */
#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 16241d2..afdbbf5 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -2512,9 +2512,11 @@
priv->random_mac[i] |= get_random_int() &
~(request->mac_addr_mask[i]);
}
+ ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac);
+ } else {
+ eth_zero_addr(priv->random_mac);
}
- ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac);
user_scan_cfg->num_ssids = request->n_ssids;
user_scan_cfg->ssid_list = request->ssids;
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index 7a310c4..1fdb86c 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -995,6 +995,7 @@
if (card && card->cmd_buf) {
mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(card->cmd_buf);
}
return 0;
}
@@ -1561,6 +1562,11 @@
return -1;
card->cmd_buf = skb;
+ /*
+ * Need to keep a reference, since core driver might free up this
+ * buffer before we've unmapped it.
+ */
+ skb_get(skb);
/* To send a command, the driver will:
1. Write the 64bit physical address of the data buffer to
@@ -1658,6 +1664,7 @@
if (card->cmd_buf) {
mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(card->cmd_buf);
card->cmd_buf = NULL;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
index 5dad4021..a74fad6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
@@ -359,6 +359,107 @@
return rtl8821ae_phy_rf6052_config(hw);
}
+static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp;
+
+ switch (rtlhal->rfe_type) {
+ case 3:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
+ break;
+ case 4:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001);
+ break;
+ case 5:
+ rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
+ rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ case 1:
+ if (rtlpriv->btcoexist.bt_coexistence) {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ }
+ case 0:
+ case 2:
+ default:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ break;
+ }
+}
+
+static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 tmp;
+
+ switch (rtlhal->rfe_type) {
+ case 0:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ case 1:
+ if (rtlpriv->btcoexist.bt_coexistence) {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ } else {
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
+ 0x77337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
+ }
+ break;
+ case 3:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
+ break;
+ case 5:
+ rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
+ rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ case 2:
+ case 4:
+ default:
+ rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
+ rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
+ rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
+ break;
+ }
+}
+
u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band,
u8 rf_path)
{
@@ -553,14 +654,9 @@
/* 0x82C[1:0] = 2b'00 */
rtl_set_bbreg(hw, 0x82c, 0x3, 0);
}
- if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
- rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
- 0x77777777);
- rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
- 0x77777777);
- rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x000);
- rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x000);
- }
+
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
+ _rtl8812ae_phy_set_rfe_reg_24g(hw);
rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1);
rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1);
@@ -615,14 +711,8 @@
/* 0x82C[1:0] = 2'b00 */
rtl_set_bbreg(hw, 0x82c, 0x3, 1);
- if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
- rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
- 0x77337777);
- rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
- 0x77337777);
- rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x010);
- rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x010);
- }
+ if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
+ _rtl8812ae_phy_set_rfe_reg_5g(hw);
rtl_set_bbreg(hw, RTXPATH, 0xf0, 0);
rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
index 1d6110f..ed69dbe 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h
@@ -2424,6 +2424,7 @@
#define BMASKH4BITS 0xf0000000
#define BMASKOFDM_D 0xffc00000
#define BMASKCCK 0x3f3f3f3f
+#define BMASKRFEINV 0x3ff00000
#define BRFREGOFFSETMASK 0xfffff
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 9d23692..e1c6f99 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -70,3 +70,11 @@
source "drivers/nfc/s3fwrn5/Kconfig"
source "drivers/nfc/st95hf/Kconfig"
endmenu
+
+config NFC_NQ
+ tristate "QTI NCI based NFC Controller Driver for NQx"
+ depends on I2C
+ help
+ This enables the NFC driver for NQx based devices.
+ This is for i2c connected version. NCI protocol logic
+ resides in the usermode and it has no other NFC dependencies.
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index bab8ef0..b691fd4 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -17,3 +17,4 @@
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/
obj-$(CONFIG_NFC_ST95HF) += st95hf/
+obj-$(CONFIG_NFC_NQ) += nq-nci.o
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
new file mode 100644
index 0000000..baa4f94
--- /dev/null
+++ b/drivers/nfc/nq-nci.c
@@ -0,0 +1,1242 @@
+/* Copyright (c) 2015-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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/uaccess.h>
+#include "nq-nci.h"
+#include <linux/clk.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+
+struct nqx_platform_data {
+ unsigned int irq_gpio;
+ unsigned int en_gpio;
+ unsigned int clkreq_gpio;
+ unsigned int firm_gpio;
+ unsigned int ese_gpio;
+ const char *clk_src_name;
+};
+
+static const struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,nq-nci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_match_table);
+
+#define MAX_BUFFER_SIZE (320)
+#define WAKEUP_SRC_TIMEOUT (2000)
+#define MAX_RETRY_COUNT 3
+
+struct nqx_dev {
+ wait_queue_head_t read_wq;
+ struct mutex read_mutex;
+ struct i2c_client *client;
+ struct miscdevice nqx_device;
+ union nqx_uinfo nqx_info;
+ /* NFC GPIO variables */
+ unsigned int irq_gpio;
+ unsigned int en_gpio;
+ unsigned int firm_gpio;
+ unsigned int clkreq_gpio;
+ unsigned int ese_gpio;
+ /* NFC VEN pin state powered by Nfc */
+ bool nfc_ven_enabled;
+ /* NFC_IRQ state */
+ bool irq_enabled;
+ /* NFC_IRQ wake-up state */
+ bool irq_wake_up;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+ /* Initial CORE RESET notification */
+ unsigned int core_reset_ntf;
+ /* CLK control */
+ bool clk_run;
+ struct clk *s_clk;
+ /* read buffer*/
+ size_t kbuflen;
+ u8 *kbuf;
+ struct nqx_platform_data *pdata;
+};
+
+static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v);
+/*clock enable function*/
+static int nqx_clock_select(struct nqx_dev *nqx_dev);
+/*clock disable function*/
+static int nqx_clock_deselect(struct nqx_dev *nqx_dev);
+static struct notifier_block nfcc_notifier = {
+ .notifier_call = nfcc_reboot,
+ .next = NULL,
+ .priority = 0
+};
+
+unsigned int disable_ctrl;
+
+static void nqx_init_stat(struct nqx_dev *nqx_dev)
+{
+ nqx_dev->count_irq = 0;
+}
+
+static void nqx_disable_irq(struct nqx_dev *nqx_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nqx_dev->irq_enabled_lock, flags);
+ if (nqx_dev->irq_enabled) {
+ disable_irq_nosync(nqx_dev->client->irq);
+ nqx_dev->irq_enabled = false;
+ }
+ spin_unlock_irqrestore(&nqx_dev->irq_enabled_lock, flags);
+}
+
+/**
+ * nqx_enable_irq()
+ *
+ * Check if interrupt is enabled or not
+ * and enable interrupt
+ *
+ * Return: void
+ */
+static void nqx_enable_irq(struct nqx_dev *nqx_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nqx_dev->irq_enabled_lock, flags);
+ if (!nqx_dev->irq_enabled) {
+ nqx_dev->irq_enabled = true;
+ enable_irq(nqx_dev->client->irq);
+ }
+ spin_unlock_irqrestore(&nqx_dev->irq_enabled_lock, flags);
+}
+
+static irqreturn_t nqx_dev_irq_handler(int irq, void *dev_id)
+{
+ struct nqx_dev *nqx_dev = dev_id;
+ unsigned long flags;
+
+ if (device_may_wakeup(&nqx_dev->client->dev))
+ pm_wakeup_event(&nqx_dev->client->dev, WAKEUP_SRC_TIMEOUT);
+
+ nqx_disable_irq(nqx_dev);
+ spin_lock_irqsave(&nqx_dev->irq_enabled_lock, flags);
+ nqx_dev->count_irq++;
+ spin_unlock_irqrestore(&nqx_dev->irq_enabled_lock, flags);
+ wake_up(&nqx_dev->read_wq);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t nfc_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct nqx_dev *nqx_dev = filp->private_data;
+ unsigned char *tmp = NULL;
+ int ret;
+ int irq_gpio_val = 0;
+
+ if (!nqx_dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (count > nqx_dev->kbuflen)
+ count = nqx_dev->kbuflen;
+
+ dev_dbg(&nqx_dev->client->dev, "%s : reading %zu bytes.\n",
+ __func__, count);
+
+ mutex_lock(&nqx_dev->read_mutex);
+
+ irq_gpio_val = gpio_get_value(nqx_dev->irq_gpio);
+ if (irq_gpio_val == 0) {
+ if (filp->f_flags & O_NONBLOCK) {
+ dev_err(&nqx_dev->client->dev,
+ ":f_falg has O_NONBLOCK. EAGAIN\n");
+ ret = -EAGAIN;
+ goto err;
+ }
+ while (1) {
+ ret = 0;
+ if (!nqx_dev->irq_enabled) {
+ nqx_dev->irq_enabled = true;
+ enable_irq(nqx_dev->client->irq);
+ }
+ if (!gpio_get_value(nqx_dev->irq_gpio)) {
+ ret = wait_event_interruptible(nqx_dev->read_wq,
+ !nqx_dev->irq_enabled);
+ }
+ if (ret)
+ goto err;
+ nqx_disable_irq(nqx_dev);
+
+ if (gpio_get_value(nqx_dev->irq_gpio))
+ break;
+ dev_err_ratelimited(&nqx_dev->client->dev,
+ "gpio is low, no need to read data\n");
+ }
+ }
+
+ tmp = nqx_dev->kbuf;
+ if (!tmp) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: device doesn't exist anymore\n", __func__);
+ ret = -ENODEV;
+ goto err;
+ }
+ memset(tmp, 0x00, count);
+
+ /* Read data */
+ ret = i2c_master_recv(nqx_dev->client, tmp, count);
+ if (ret < 0) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: i2c_master_recv returned %d\n", __func__, ret);
+ goto err;
+ }
+ if (ret > count) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: received too many bytes from i2c (%d)\n",
+ __func__, ret);
+ ret = -EIO;
+ goto err;
+ }
+#ifdef NFC_KERNEL_BU
+ dev_dbg(&nqx_dev->client->dev, "%s : NfcNciRx %x %x %x\n",
+ __func__, tmp[0], tmp[1], tmp[2]);
+#endif
+ if (copy_to_user(buf, tmp, ret)) {
+ dev_warn(&nqx_dev->client->dev,
+ "%s : failed to copy to user space\n", __func__);
+ ret = -EFAULT;
+ goto err;
+ }
+ mutex_unlock(&nqx_dev->read_mutex);
+ return ret;
+
+err:
+ mutex_unlock(&nqx_dev->read_mutex);
+out:
+ return ret;
+}
+
+static ssize_t nfc_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct nqx_dev *nqx_dev = filp->private_data;
+ char *tmp = NULL;
+ int ret = 0;
+
+ if (!nqx_dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (count > nqx_dev->kbuflen) {
+ dev_err(&nqx_dev->client->dev, "%s: out of memory\n",
+ __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tmp = memdup_user(buf, count);
+ if (IS_ERR(tmp)) {
+ dev_err(&nqx_dev->client->dev, "%s: memdup_user failed\n",
+ __func__);
+ ret = PTR_ERR(tmp);
+ goto out;
+ }
+
+ ret = i2c_master_send(nqx_dev->client, tmp, count);
+ if (ret != count) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: failed to write %d\n", __func__, ret);
+ ret = -EIO;
+ goto out_free;
+ }
+#ifdef NFC_KERNEL_BU
+ dev_dbg(&nqx_dev->client->dev,
+ "%s : i2c-%d: NfcNciTx %x %x %x\n",
+ __func__, iminor(file_inode(filp)),
+ tmp[0], tmp[1], tmp[2]);
+#endif
+ usleep_range(1000, 1100);
+out_free:
+ kfree(tmp);
+out:
+ return ret;
+}
+
+/**
+ * nqx_standby_write()
+ * @buf: pointer to data buffer
+ * @len: # of bytes need to transfer
+ *
+ * write data buffer over I2C and retry
+ * if NFCC is in stand by mode
+ *
+ * Return: # of bytes written or -ve value in case of error
+ */
+static int nqx_standby_write(struct nqx_dev *nqx_dev,
+ const unsigned char *buf, size_t len)
+{
+ int ret = -EINVAL;
+ int retry_cnt;
+
+ for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) {
+ ret = i2c_master_send(nqx_dev->client, buf, len);
+ if (ret < 0) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: write failed, Maybe in Standby Mode - Retry(%d)\n",
+ __func__, retry_cnt);
+ usleep_range(1000, 1100);
+ } else if (ret == len)
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Power management of the eSE
+ * NFC & eSE ON : NFC_EN high and eSE_pwr_req high.
+ * NFC OFF & eSE ON : NFC_EN high and eSE_pwr_req high.
+ * NFC OFF & eSE OFF : NFC_EN low and eSE_pwr_req low.
+ */
+static int nqx_ese_pwr(struct nqx_dev *nqx_dev, unsigned long int arg)
+{
+ int r = -1;
+ const unsigned char svdd_off_cmd_warn[] = {0x2F, 0x31, 0x01, 0x01};
+ const unsigned char svdd_off_cmd_done[] = {0x2F, 0x31, 0x01, 0x00};
+
+ if (!gpio_is_valid(nqx_dev->ese_gpio)) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: ese_gpio is not valid\n", __func__);
+ return -EINVAL;
+ }
+
+ if (arg == 0) {
+ /*
+ * We want to power on the eSE and to do so we need the
+ * eSE_pwr_req pin and the NFC_EN pin to be high
+ */
+ if (gpio_get_value(nqx_dev->ese_gpio)) {
+ dev_dbg(&nqx_dev->client->dev, "ese_gpio is already high\n");
+ r = 0;
+ } else {
+ /**
+ * Let's store the NFC_EN pin state
+ * only if the eSE is not yet on
+ */
+ nqx_dev->nfc_ven_enabled =
+ gpio_get_value(nqx_dev->en_gpio);
+ if (!nqx_dev->nfc_ven_enabled) {
+ gpio_set_value(nqx_dev->en_gpio, 1);
+ /* hardware dependent delay */
+ usleep_range(1000, 1100);
+ }
+ gpio_set_value(nqx_dev->ese_gpio, 1);
+ if (gpio_get_value(nqx_dev->ese_gpio)) {
+ dev_dbg(&nqx_dev->client->dev, "ese_gpio is enabled\n");
+ r = 0;
+ }
+ }
+ } else if (arg == 1) {
+ if (nqx_dev->nfc_ven_enabled &&
+ ((nqx_dev->nqx_info.info.chip_type == NFCC_NQ_220) ||
+ (nqx_dev->nqx_info.info.chip_type == NFCC_PN66T))) {
+ /**
+ * Let's inform the CLF we're
+ * powering off the eSE
+ */
+ r = nqx_standby_write(nqx_dev, svdd_off_cmd_warn,
+ sizeof(svdd_off_cmd_warn));
+ if (r < 0) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: write failed after max retry\n",
+ __func__);
+ return -ENXIO;
+ }
+ dev_dbg(&nqx_dev->client->dev,
+ "%s: svdd_off_cmd_warn sent\n", __func__);
+
+ /* let's power down the eSE */
+ gpio_set_value(nqx_dev->ese_gpio, 0);
+ dev_dbg(&nqx_dev->client->dev,
+ "%s: nqx_dev->ese_gpio set to 0\n", __func__);
+
+ /**
+ * Time needed for the SVDD capacitor
+ * to get discharged
+ */
+ usleep_range(8000, 8100);
+
+ /* Let's inform the CLF the eSE is now off */
+ r = nqx_standby_write(nqx_dev, svdd_off_cmd_done,
+ sizeof(svdd_off_cmd_done));
+ if (r < 0) {
+ dev_err(&nqx_dev->client->dev,
+ "%s: write failed after max retry\n",
+ __func__);
+ return -ENXIO;
+ }
+ dev_dbg(&nqx_dev->client->dev,
+ "%s: svdd_off_cmd_done sent\n", __func__);
+ } else {
+ /**
+ * In case the NFC is off,
+ * there's no need to send the i2c commands
+ */
+ gpio_set_value(nqx_dev->ese_gpio, 0);
+ }
+
+ if (!gpio_get_value(nqx_dev->ese_gpio)) {
+ dev_dbg(&nqx_dev->client->dev, "ese_gpio is disabled\n");
+ r = 0;
+ }
+
+ if (!nqx_dev->nfc_ven_enabled) {
+ /* hardware dependent delay */
+ usleep_range(1000, 1100);
+ dev_dbg(&nqx_dev->client->dev, "disabling en_gpio\n");
+ gpio_set_value(nqx_dev->en_gpio, 0);
+ }
+ } else if (arg == 3) {
+ r = gpio_get_value(nqx_dev->ese_gpio);
+ }
+ return r;
+}
+
+static int nfc_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct nqx_dev *nqx_dev = container_of(filp->private_data,
+ struct nqx_dev, nqx_device);
+
+ filp->private_data = nqx_dev;
+ nqx_init_stat(nqx_dev);
+
+ dev_dbg(&nqx_dev->client->dev,
+ "%s: %d,%d\n", __func__, imajor(inode), iminor(inode));
+ return ret;
+}
+
+/*
+ * nfc_ioctl_power_states() - power control
+ * @filp: pointer to the file descriptor
+ * @arg: mode that we want to move to
+ *
+ * Device power control. Depending on the arg value, device moves to
+ * different states
+ * (arg = 0): NFC_ENABLE GPIO = 0, FW_DL GPIO = 0
+ * (arg = 1): NFC_ENABLE GPIO = 1, FW_DL GPIO = 0
+ * (arg = 2): FW_DL GPIO = 1
+ *
+ * Return: -ENOIOCTLCMD if arg is not supported, 0 in any other case
+ */
+int nfc_ioctl_power_states(struct file *filp, unsigned long arg)
+{
+ int r = 0;
+ struct nqx_dev *nqx_dev = filp->private_data;
+
+ if (arg == 0) {
+ /*
+ * We are attempting a hardware reset so let us disable
+ * interrupts to avoid spurious notifications to upper
+ * layers.
+ */
+ nqx_disable_irq(nqx_dev);
+ dev_dbg(&nqx_dev->client->dev,
+ "gpio_set_value disable: %s: info: %p\n",
+ __func__, nqx_dev);
+ if (gpio_is_valid(nqx_dev->firm_gpio))
+ gpio_set_value(nqx_dev->firm_gpio, 0);
+
+ if (gpio_is_valid(nqx_dev->ese_gpio)) {
+ if (!gpio_get_value(nqx_dev->ese_gpio)) {
+ dev_dbg(&nqx_dev->client->dev, "disabling en_gpio\n");
+ gpio_set_value(nqx_dev->en_gpio, 0);
+ } else {
+ dev_dbg(&nqx_dev->client->dev, "keeping en_gpio high\n");
+ }
+ } else {
+ dev_dbg(&nqx_dev->client->dev, "ese_gpio invalid, set en_gpio to low\n");
+ gpio_set_value(nqx_dev->en_gpio, 0);
+ }
+ r = nqx_clock_deselect(nqx_dev);
+ if (r < 0)
+ dev_err(&nqx_dev->client->dev, "unable to disable clock\n");
+ nqx_dev->nfc_ven_enabled = false;
+ /* hardware dependent delay */
+ msleep(100);
+ } else if (arg == 1) {
+ nqx_enable_irq(nqx_dev);
+ dev_dbg(&nqx_dev->client->dev,
+ "gpio_set_value enable: %s: info: %p\n",
+ __func__, nqx_dev);
+ if (gpio_is_valid(nqx_dev->firm_gpio))
+ gpio_set_value(nqx_dev->firm_gpio, 0);
+ gpio_set_value(nqx_dev->en_gpio, 1);
+ r = nqx_clock_select(nqx_dev);
+ if (r < 0)
+ dev_err(&nqx_dev->client->dev, "unable to enable clock\n");
+ nqx_dev->nfc_ven_enabled = true;
+ msleep(20);
+ } else if (arg == 2) {
+ /*
+ * We are switching to Dowload Mode, toggle the enable pin
+ * in order to set the NFCC in the new mode
+ */
+ if (gpio_is_valid(nqx_dev->ese_gpio)) {
+ if (gpio_get_value(nqx_dev->ese_gpio)) {
+ dev_err(&nqx_dev->client->dev,
+ "FW download forbidden while ese is on\n");
+ return -EBUSY; /* Device or resource busy */
+ }
+ }
+ gpio_set_value(nqx_dev->en_gpio, 1);
+ msleep(20);
+ if (gpio_is_valid(nqx_dev->firm_gpio))
+ gpio_set_value(nqx_dev->firm_gpio, 1);
+ msleep(20);
+ gpio_set_value(nqx_dev->en_gpio, 0);
+ msleep(100);
+ gpio_set_value(nqx_dev->en_gpio, 1);
+ msleep(20);
+ } else {
+ r = -ENOIOCTLCMD;
+ }
+
+ return r;
+}
+
+#ifdef CONFIG_COMPAT
+static long nfc_compat_ioctl(struct file *pfile, unsigned int cmd,
+ unsigned long arg)
+{
+ long r = 0;
+
+ arg = (compat_u64)arg;
+ switch (cmd) {
+ case NFC_SET_PWR:
+ nfc_ioctl_power_states(pfile, arg);
+ break;
+ case ESE_SET_PWR:
+ nqx_ese_pwr(pfile->private_data, arg);
+ break;
+ case ESE_GET_PWR:
+ nqx_ese_pwr(pfile->private_data, 3);
+ break;
+ case SET_RX_BLOCK:
+ break;
+ case SET_EMULATOR_TEST_POINT:
+ break;
+ default:
+ r = -ENOTTY;
+ }
+ return r;
+}
+#endif
+
+/*
+ * nfc_ioctl_core_reset_ntf()
+ * @filp: pointer to the file descriptor
+ *
+ * Allows callers to determine if a CORE_RESET_NTF has arrived
+ *
+ * Return: the value of variable core_reset_ntf
+ */
+int nfc_ioctl_core_reset_ntf(struct file *filp)
+{
+ struct nqx_dev *nqx_dev = filp->private_data;
+
+ dev_dbg(&nqx_dev->client->dev, "%s: returning = %d\n", __func__,
+ nqx_dev->core_reset_ntf);
+ return nqx_dev->core_reset_ntf;
+}
+
+/*
+ * Inside nfc_ioctl_nfcc_info
+ *
+ * @brief nfc_ioctl_nfcc_info
+ *
+ * Check the NQ Chipset and firmware version details
+ */
+unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg)
+{
+ unsigned int r = 0;
+ struct nqx_dev *nqx_dev = filp->private_data;
+
+ r = nqx_dev->nqx_info.i;
+ dev_dbg(&nqx_dev->client->dev,
+ "nqx nfc : nfc_ioctl_nfcc_info r = %d\n", r);
+
+ return r;
+}
+
+static long nfc_ioctl(struct file *pfile, unsigned int cmd,
+ unsigned long arg)
+{
+ int r = 0;
+
+ switch (cmd) {
+ case NFC_SET_PWR:
+ r = nfc_ioctl_power_states(pfile, arg);
+ break;
+ case ESE_SET_PWR:
+ r = nqx_ese_pwr(pfile->private_data, arg);
+ break;
+ case ESE_GET_PWR:
+ r = nqx_ese_pwr(pfile->private_data, 3);
+ break;
+ case SET_RX_BLOCK:
+ break;
+ case SET_EMULATOR_TEST_POINT:
+ break;
+ case NFCC_INITIAL_CORE_RESET_NTF:
+ r = nfc_ioctl_core_reset_ntf(pfile);
+ break;
+ case NFCC_GET_INFO:
+ r = nfc_ioctl_nfcc_info(pfile, arg);
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ }
+ return r;
+}
+
+static const struct file_operations nfc_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = nfc_read,
+ .write = nfc_write,
+ .open = nfc_open,
+ .unlocked_ioctl = nfc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = nfc_compat_ioctl
+#endif
+};
+
+/* Check for availability of NQ_ NFC controller hardware */
+static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev)
+{
+ int ret = 0;
+
+ unsigned char raw_nci_reset_cmd[] = {0x20, 0x00, 0x01, 0x00};
+ unsigned char raw_nci_init_cmd[] = {0x20, 0x01, 0x00};
+ unsigned char nci_init_rsp[28];
+ unsigned char nci_reset_rsp[6];
+ unsigned char init_rsp_len = 0;
+ unsigned int enable_gpio = nqx_dev->en_gpio;
+ /* making sure that the NFCC starts in a clean state. */
+ gpio_set_value(enable_gpio, 0);/* ULPM: Disable */
+ /* hardware dependent delay */
+ msleep(20);
+ gpio_set_value(enable_gpio, 1);/* HPD : Enable*/
+ /* hardware dependent delay */
+ msleep(20);
+
+ /* send NCI CORE RESET CMD with Keep Config parameters */
+ ret = i2c_master_send(client, raw_nci_reset_cmd,
+ sizeof(raw_nci_reset_cmd));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_send Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ /* hardware dependent delay */
+ msleep(30);
+
+ /* Read Response of RESET command */
+ ret = i2c_master_recv(client, nci_reset_rsp,
+ sizeof(nci_reset_rsp));
+ dev_err(&client->dev,
+ "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
+ __func__, nci_reset_rsp[0],
+ nci_reset_rsp[1], nci_reset_rsp[2]);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_recv Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ ret = i2c_master_send(client, raw_nci_init_cmd,
+ sizeof(raw_nci_init_cmd));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_send Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ /* hardware dependent delay */
+ msleep(30);
+ /* Read Response of INIT command */
+ ret = i2c_master_recv(client, nci_init_rsp,
+ sizeof(nci_init_rsp));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: - i2c_master_recv Error\n", __func__);
+ goto err_nfcc_hw_check;
+ }
+ init_rsp_len = 2 + nci_init_rsp[2]; /*payload + len*/
+ if (init_rsp_len > PAYLOAD_HEADER_LENGTH) {
+ nqx_dev->nqx_info.info.chip_type =
+ nci_init_rsp[init_rsp_len - 3];
+ nqx_dev->nqx_info.info.rom_version =
+ nci_init_rsp[init_rsp_len - 2];
+ nqx_dev->nqx_info.info.fw_major =
+ nci_init_rsp[init_rsp_len - 1];
+ nqx_dev->nqx_info.info.fw_minor =
+ nci_init_rsp[init_rsp_len];
+ }
+ dev_dbg(&nqx_dev->client->dev, "NQ NFCC chip_type = %x\n",
+ nqx_dev->nqx_info.info.chip_type);
+ dev_dbg(&nqx_dev->client->dev, "NQ fw version = %x.%x.%x\n",
+ nqx_dev->nqx_info.info.rom_version,
+ nqx_dev->nqx_info.info.fw_major,
+ nqx_dev->nqx_info.info.fw_minor);
+
+ switch (nqx_dev->nqx_info.info.chip_type) {
+ case NFCC_NQ_210:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ210 ##\n", __func__);
+ break;
+ case NFCC_NQ_220:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ220 ##\n", __func__);
+ break;
+ case NFCC_NQ_310:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ310 ##\n", __func__);
+ break;
+ case NFCC_NQ_330:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == NQ330 ##\n", __func__);
+ break;
+ case NFCC_PN66T:
+ dev_dbg(&client->dev,
+ "%s: ## NFCC == PN66T ##\n", __func__);
+ break;
+ default:
+ dev_err(&client->dev,
+ "%s: - NFCC HW not Supported\n", __func__);
+ break;
+ }
+
+ /*Disable NFC by default to save power on boot*/
+ gpio_set_value(enable_gpio, 0);/* ULPM: Disable */
+ ret = 0;
+ goto done;
+
+err_nfcc_hw_check:
+ ret = -ENXIO;
+ dev_err(&client->dev,
+ "%s: - NFCC HW not available\n", __func__);
+done:
+ return ret;
+}
+
+/*
+ * Routine to enable clock.
+ * this routine can be extended to select from multiple
+ * sources based on clk_src_name.
+ */
+static int nqx_clock_select(struct nqx_dev *nqx_dev)
+{
+ int r = 0;
+
+ nqx_dev->s_clk = clk_get(&nqx_dev->client->dev, "ref_clk");
+
+ if (nqx_dev->s_clk == NULL)
+ goto err_clk;
+
+ if (nqx_dev->clk_run == false)
+ r = clk_prepare_enable(nqx_dev->s_clk);
+
+ if (r)
+ goto err_clk;
+
+ nqx_dev->clk_run = true;
+
+ return r;
+
+err_clk:
+ r = -1;
+ return r;
+}
+
+/*
+ * Routine to disable clocks
+ */
+static int nqx_clock_deselect(struct nqx_dev *nqx_dev)
+{
+ int r = -1;
+
+ if (nqx_dev->s_clk != NULL) {
+ if (nqx_dev->clk_run == true) {
+ clk_disable_unprepare(nqx_dev->s_clk);
+ nqx_dev->clk_run = false;
+ }
+ return 0;
+ }
+ return r;
+}
+
+static int nfc_parse_dt(struct device *dev, struct nqx_platform_data *pdata)
+{
+ int r = 0;
+ struct device_node *np = dev->of_node;
+
+ pdata->en_gpio = of_get_named_gpio(np, "qcom,nq-ven", 0);
+ if ((!gpio_is_valid(pdata->en_gpio)))
+ return -EINVAL;
+ disable_ctrl = pdata->en_gpio;
+
+ pdata->irq_gpio = of_get_named_gpio(np, "qcom,nq-irq", 0);
+ if ((!gpio_is_valid(pdata->irq_gpio)))
+ return -EINVAL;
+
+ pdata->firm_gpio = of_get_named_gpio(np, "qcom,nq-firm", 0);
+ if (!gpio_is_valid(pdata->firm_gpio)) {
+ dev_warn(dev,
+ "FIRM GPIO <OPTIONAL> error getting from OF node\n");
+ pdata->firm_gpio = -EINVAL;
+ }
+
+ pdata->ese_gpio = of_get_named_gpio(np, "qcom,nq-esepwr", 0);
+ if (!gpio_is_valid(pdata->ese_gpio)) {
+ dev_warn(dev,
+ "ese GPIO <OPTIONAL> error getting from OF node\n");
+ pdata->ese_gpio = -EINVAL;
+ }
+
+ r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name);
+
+ pdata->clkreq_gpio = of_get_named_gpio(np, "qcom,nq-clkreq", 0);
+
+ if (r)
+ return -EINVAL;
+ return r;
+}
+
+static inline int gpio_input_init(const struct device * const dev,
+ const int gpio, const char * const gpio_name)
+{
+ int r = gpio_request(gpio, gpio_name);
+
+ if (r) {
+ dev_err(dev, "unable to request gpio [%d]\n", gpio);
+ return r;
+ }
+
+ r = gpio_direction_input(gpio);
+ if (r)
+ dev_err(dev, "unable to set direction for gpio [%d]\n", gpio);
+
+ return r;
+}
+
+static int nqx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int r = 0;
+ int irqn = 0;
+ struct nqx_platform_data *platform_data;
+ struct nqx_dev *nqx_dev;
+
+ dev_dbg(&client->dev, "%s: enter\n", __func__);
+ if (client->dev.of_node) {
+ platform_data = devm_kzalloc(&client->dev,
+ sizeof(struct nqx_platform_data), GFP_KERNEL);
+ if (!platform_data) {
+ r = -ENOMEM;
+ goto err_platform_data;
+ }
+ r = nfc_parse_dt(&client->dev, platform_data);
+ if (r)
+ goto err_free_data;
+ } else
+ platform_data = client->dev.platform_data;
+
+ dev_dbg(&client->dev,
+ "%s, inside nfc-nci flags = %x\n",
+ __func__, client->flags);
+
+ if (platform_data == NULL) {
+ dev_err(&client->dev, "%s: failed\n", __func__);
+ r = -ENODEV;
+ goto err_platform_data;
+ }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "%s: need I2C_FUNC_I2C\n", __func__);
+ r = -ENODEV;
+ goto err_free_data;
+ }
+ nqx_dev = kzalloc(sizeof(*nqx_dev), GFP_KERNEL);
+ if (nqx_dev == NULL) {
+ r = -ENOMEM;
+ goto err_free_data;
+ }
+ nqx_dev->client = client;
+ nqx_dev->kbuflen = MAX_BUFFER_SIZE;
+ nqx_dev->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!nqx_dev->kbuf) {
+ dev_err(&client->dev,
+ "failed to allocate memory for nqx_dev->kbuf\n");
+ r = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ if (gpio_is_valid(platform_data->en_gpio)) {
+ r = gpio_request(platform_data->en_gpio, "nfc_reset_gpio");
+ if (r) {
+ dev_err(&client->dev,
+ "%s: unable to request nfc reset gpio [%d]\n",
+ __func__,
+ platform_data->en_gpio);
+ goto err_mem;
+ }
+ r = gpio_direction_output(platform_data->en_gpio, 0);
+ if (r) {
+ dev_err(&client->dev,
+ "%s: unable to set direction for nfc reset gpio [%d]\n",
+ __func__,
+ platform_data->en_gpio);
+ goto err_en_gpio;
+ }
+ } else {
+ dev_err(&client->dev,
+ "%s: nfc reset gpio not provided\n", __func__);
+ goto err_mem;
+ }
+
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
+ if (r) {
+ dev_err(&client->dev, "%s: unable to request nfc irq gpio [%d]\n",
+ __func__, platform_data->irq_gpio);
+ goto err_en_gpio;
+ }
+ r = gpio_direction_input(platform_data->irq_gpio);
+ if (r) {
+ dev_err(&client->dev,
+ "%s: unable to set direction for nfc irq gpio [%d]\n",
+ __func__,
+ platform_data->irq_gpio);
+ goto err_irq_gpio;
+ }
+ irqn = gpio_to_irq(platform_data->irq_gpio);
+ if (irqn < 0) {
+ r = irqn;
+ goto err_irq_gpio;
+ }
+ client->irq = irqn;
+ } else {
+ dev_err(&client->dev, "%s: irq gpio not provided\n", __func__);
+ goto err_en_gpio;
+ }
+ if (gpio_is_valid(platform_data->firm_gpio)) {
+ r = gpio_request(platform_data->firm_gpio,
+ "nfc_firm_gpio");
+ if (r) {
+ dev_err(&client->dev,
+ "%s: unable to request nfc firmware gpio [%d]\n",
+ __func__, platform_data->firm_gpio);
+ goto err_irq_gpio;
+ }
+ r = gpio_direction_output(platform_data->firm_gpio, 0);
+ if (r) {
+ dev_err(&client->dev,
+ "%s: cannot set direction for nfc firmware gpio [%d]\n",
+ __func__, platform_data->firm_gpio);
+ goto err_firm_gpio;
+ }
+ } else {
+ dev_err(&client->dev,
+ "%s: firm gpio not provided\n", __func__);
+ goto err_irq_gpio;
+ }
+ if (gpio_is_valid(platform_data->ese_gpio)) {
+ r = gpio_request(platform_data->ese_gpio,
+ "nfc-ese_pwr");
+ if (r) {
+ nqx_dev->ese_gpio = -EINVAL;
+ dev_err(&client->dev,
+ "%s: unable to request nfc ese gpio [%d]\n",
+ __func__, platform_data->ese_gpio);
+ /* ese gpio optional so we should continue */
+ } else {
+ nqx_dev->ese_gpio = platform_data->ese_gpio;
+ r = gpio_direction_output(platform_data->ese_gpio, 0);
+ if (r) {
+ /*
+ * free ese gpio and set invalid
+ * to avoid further use
+ */
+ gpio_free(platform_data->ese_gpio);
+ nqx_dev->ese_gpio = -EINVAL;
+ dev_err(&client->dev,
+ "%s: cannot set direction for nfc ese gpio [%d]\n",
+ __func__, platform_data->ese_gpio);
+ /* ese gpio optional so we should continue */
+ }
+ }
+ } else {
+ nqx_dev->ese_gpio = -EINVAL;
+ dev_err(&client->dev,
+ "%s: ese gpio not provided\n", __func__);
+ /* ese gpio optional so we should continue */
+ }
+ if (gpio_is_valid(platform_data->clkreq_gpio)) {
+ r = gpio_request(platform_data->clkreq_gpio,
+ "nfc_clkreq_gpio");
+ if (r) {
+ dev_err(&client->dev,
+ "%s: unable to request nfc clkreq gpio [%d]\n",
+ __func__, platform_data->clkreq_gpio);
+ goto err_ese_gpio;
+ }
+ r = gpio_direction_input(platform_data->clkreq_gpio);
+ if (r) {
+ dev_err(&client->dev,
+ "%s: cannot set direction for nfc clkreq gpio [%d]\n",
+ __func__, platform_data->clkreq_gpio);
+ goto err_clkreq_gpio;
+ }
+ } else {
+ dev_err(&client->dev,
+ "%s: clkreq gpio not provided\n", __func__);
+ goto err_ese_gpio;
+ }
+
+ nqx_dev->en_gpio = platform_data->en_gpio;
+ nqx_dev->irq_gpio = platform_data->irq_gpio;
+ nqx_dev->firm_gpio = platform_data->firm_gpio;
+ nqx_dev->clkreq_gpio = platform_data->clkreq_gpio;
+ nqx_dev->pdata = platform_data;
+
+ /* init mutex and queues */
+ init_waitqueue_head(&nqx_dev->read_wq);
+ mutex_init(&nqx_dev->read_mutex);
+ spin_lock_init(&nqx_dev->irq_enabled_lock);
+
+ nqx_dev->nqx_device.minor = MISC_DYNAMIC_MINOR;
+ nqx_dev->nqx_device.name = "nq-nci";
+ nqx_dev->nqx_device.fops = &nfc_dev_fops;
+
+ r = misc_register(&nqx_dev->nqx_device);
+ if (r) {
+ dev_err(&client->dev, "%s: misc_register failed\n", __func__);
+ goto err_misc_register;
+ }
+
+ /* NFC_INT IRQ */
+ nqx_dev->irq_enabled = true;
+ r = request_irq(client->irq, nqx_dev_irq_handler,
+ IRQF_TRIGGER_HIGH, client->name, nqx_dev);
+ if (r) {
+ dev_err(&client->dev, "%s: request_irq failed\n", __func__);
+ goto err_request_irq_failed;
+ }
+ nqx_disable_irq(nqx_dev);
+
+ /*
+ * To be efficient we need to test whether nfcc hardware is physically
+ * present before attempting further hardware initialisation.
+ *
+ */
+ r = nfcc_hw_check(client, nqx_dev);
+ if (r) {
+ /* make sure NFCC is not enabled */
+ gpio_set_value(platform_data->en_gpio, 0);
+ /* We don't think there is hardware switch NFC OFF */
+ goto err_request_hw_check_failed;
+ }
+
+ /* Register reboot notifier here */
+ r = register_reboot_notifier(&nfcc_notifier);
+ if (r) {
+ dev_err(&client->dev,
+ "%s: cannot register reboot notifier(err = %d)\n",
+ __func__, r);
+ /*
+ * nfcc_hw_check function not doing memory
+ * allocation so using same goto target here
+ */
+ goto err_request_hw_check_failed;
+ }
+
+#ifdef NFC_KERNEL_BU
+ r = nqx_clock_select(nqx_dev);
+ if (r < 0) {
+ dev_err(&client->dev,
+ "%s: nqx_clock_select failed\n", __func__);
+ goto err_clock_en_failed;
+ }
+ gpio_set_value(platform_data->en_gpio, 1);
+#endif
+ device_init_wakeup(&client->dev, true);
+ device_set_wakeup_capable(&client->dev, true);
+ i2c_set_clientdata(client, nqx_dev);
+ nqx_dev->irq_wake_up = false;
+
+ dev_err(&client->dev,
+ "%s: probing NFCC NQxxx exited successfully\n",
+ __func__);
+ return 0;
+
+#ifdef NFC_KERNEL_BU
+err_clock_en_failed:
+ unregister_reboot_notifier(&nfcc_notifier);
+#endif
+err_request_hw_check_failed:
+ free_irq(client->irq, nqx_dev);
+err_request_irq_failed:
+ misc_deregister(&nqx_dev->nqx_device);
+err_misc_register:
+ mutex_destroy(&nqx_dev->read_mutex);
+err_clkreq_gpio:
+ gpio_free(platform_data->clkreq_gpio);
+err_ese_gpio:
+ /* optional gpio, not sure was configured in probe */
+ if (nqx_dev->ese_gpio > 0)
+ gpio_free(platform_data->ese_gpio);
+err_firm_gpio:
+ gpio_free(platform_data->firm_gpio);
+err_irq_gpio:
+ gpio_free(platform_data->irq_gpio);
+err_en_gpio:
+ gpio_free(platform_data->en_gpio);
+err_mem:
+ kfree(nqx_dev->kbuf);
+err_free_dev:
+ kfree(nqx_dev);
+err_free_data:
+ if (client->dev.of_node)
+ devm_kfree(&client->dev, platform_data);
+err_platform_data:
+ dev_err(&client->dev,
+ "%s: probing nqxx failed, check hardware\n",
+ __func__);
+ return r;
+}
+
+static int nqx_remove(struct i2c_client *client)
+{
+ int ret = 0;
+ struct nqx_dev *nqx_dev;
+
+ nqx_dev = i2c_get_clientdata(client);
+ if (!nqx_dev) {
+ dev_err(&client->dev,
+ "%s: device doesn't exist anymore\n", __func__);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ unregister_reboot_notifier(&nfcc_notifier);
+ free_irq(client->irq, nqx_dev);
+ misc_deregister(&nqx_dev->nqx_device);
+ mutex_destroy(&nqx_dev->read_mutex);
+ gpio_free(nqx_dev->clkreq_gpio);
+ /* optional gpio, not sure was configured in probe */
+ if (nqx_dev->ese_gpio > 0)
+ gpio_free(nqx_dev->ese_gpio);
+ gpio_free(nqx_dev->firm_gpio);
+ gpio_free(nqx_dev->irq_gpio);
+ gpio_free(nqx_dev->en_gpio);
+ kfree(nqx_dev->kbuf);
+ if (client->dev.of_node)
+ devm_kfree(&client->dev, nqx_dev->pdata);
+
+ kfree(nqx_dev);
+err:
+ return ret;
+}
+
+static int nqx_suspend(struct device *device)
+{
+ struct i2c_client *client = to_i2c_client(device);
+ struct nqx_dev *nqx_dev = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev) && nqx_dev->irq_enabled) {
+ if (!enable_irq_wake(client->irq))
+ nqx_dev->irq_wake_up = true;
+ }
+ return 0;
+}
+
+static int nqx_resume(struct device *device)
+{
+ struct i2c_client *client = to_i2c_client(device);
+ struct nqx_dev *nqx_dev = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev) && nqx_dev->irq_wake_up) {
+ if (!disable_irq_wake(client->irq))
+ nqx_dev->irq_wake_up = false;
+ }
+ return 0;
+}
+
+static const struct i2c_device_id nqx_id[] = {
+ {"nqx-i2c", 0},
+ {}
+};
+
+static const struct dev_pm_ops nfc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(nqx_suspend, nqx_resume)
+};
+
+static struct i2c_driver nqx = {
+ .id_table = nqx_id,
+ .probe = nqx_probe,
+ .remove = nqx_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nq-nci",
+ .of_match_table = msm_match_table,
+ .pm = &nfc_pm_ops,
+ },
+};
+
+static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v)
+{
+ gpio_set_value(disable_ctrl, 1);
+ return NOTIFY_OK;
+}
+
+/*
+ * module load/unload record keeping
+ */
+static int __init nqx_dev_init(void)
+{
+ return i2c_add_driver(&nqx);
+}
+module_init(nqx_dev_init);
+
+static void __exit nqx_dev_exit(void)
+{
+ unregister_reboot_notifier(&nfcc_notifier);
+ i2c_del_driver(&nqx);
+}
+module_exit(nqx_dev_exit);
+
+MODULE_DESCRIPTION("NFC nqx");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/nq-nci.h b/drivers/nfc/nq-nci.h
new file mode 100644
index 0000000..87715c2
--- /dev/null
+++ b/drivers/nfc/nq-nci.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2015-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.
+ */
+
+#ifndef __NQ_NCI_H
+#define __NQ_NCI_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <linux/semaphore.h>
+#include <linux/completion.h>
+
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/nfcinfo.h>
+
+#define NFC_SET_PWR _IOW(0xE9, 0x01, unsigned int)
+#define ESE_SET_PWR _IOW(0xE9, 0x02, unsigned int)
+#define ESE_GET_PWR _IOR(0xE9, 0x03, unsigned int)
+#define SET_RX_BLOCK _IOW(0xE9, 0x04, unsigned int)
+#define SET_EMULATOR_TEST_POINT _IOW(0xE9, 0x05, unsigned int)
+#define NFCC_INITIAL_CORE_RESET_NTF _IOW(0xE9, 0x10, unsigned int)
+
+#define NFC_RX_BUFFER_CNT_START (0x0)
+#define PAYLOAD_HEADER_LENGTH (0x3)
+#define PAYLOAD_LENGTH_MAX (256)
+#define BYTE (0x8)
+#define NCI_IDENTIFIER (0x10)
+
+enum nfcc_initial_core_reset_ntf {
+ TIMEDOUT_INITIAL_CORE_RESET_NTF = 0, /* 0*/
+ ARRIVED_INITIAL_CORE_RESET_NTF, /* 1 */
+ DEFAULT_INITIAL_CORE_RESET_NTF, /*2*/
+};
+
+enum nfcc_chip_variant {
+ NFCC_NQ_210 = 0x48, /**< NFCC NQ210 */
+ NFCC_NQ_220 = 0x58, /**< NFCC NQ220 */
+ NFCC_NQ_310 = 0x40, /**< NFCC NQ310 */
+ NFCC_NQ_330 = 0x51, /**< NFCC NQ330 */
+ NFCC_PN66T = 0x18, /**< NFCC PN66T */
+ NFCC_NOT_SUPPORTED = 0xFF /**< NFCC is not supported */
+};
+#endif
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 351bac8..0392eb8 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -218,7 +218,10 @@
if (cmd_rc < 0)
return cmd_rc;
- nvdimm_clear_from_poison_list(nvdimm_bus, phys, len);
+ if (clear_err.cleared > 0)
+ nvdimm_clear_from_poison_list(nvdimm_bus, phys,
+ clear_err.cleared);
+
return clear_err.cleared;
}
EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 8a9c186..14eac73 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -1384,6 +1384,11 @@
if (dev->cmb) {
iounmap(dev->cmb);
dev->cmb = NULL;
+ if (dev->cmbsz) {
+ sysfs_remove_file_from_group(&dev->ctrl.device->kobj,
+ &dev_attr_cmb.attr, NULL);
+ dev->cmbsz = 0;
+ }
}
}
@@ -1655,6 +1660,7 @@
{
struct pci_dev *pdev = to_pci_dev(dev->dev);
+ nvme_release_cmb(dev);
pci_free_irq_vectors(pdev);
if (pci_is_enabled(pdev)) {
@@ -1993,7 +1999,6 @@
nvme_dev_disable(dev, true);
nvme_dev_remove_admin(dev);
nvme_free_queues(dev, 0);
- nvme_release_cmb(dev);
nvme_release_prp_pools(dev);
nvme_dev_unmap(dev);
nvme_put_ctrl(&dev->ctrl);
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 3723f57..2c1b08a 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -263,7 +263,7 @@
if (!parser->range || parser->range + parser->np > parser->end)
return NULL;
- range->pci_space = parser->range[0];
+ range->pci_space = be32_to_cpup(parser->range);
range->flags = of_bus_pci_get_flags(parser->range);
range->pci_addr = of_read_number(parser->range + 1, ns);
range->cpu_addr = of_translate_address(parser->node,
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 8668808..66af185 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -505,6 +505,9 @@
/* Allocate memory for the expanded device tree */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
+ if (!mem)
+ return NULL;
+
memset(mem, 0, size);
*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c
index a53982a..2db1f7a 100644
--- a/drivers/of/of_numa.c
+++ b/drivers/of/of_numa.c
@@ -57,6 +57,8 @@
else
node_set(nid, numa_nodes_parsed);
}
+
+ of_node_put(cpus);
}
static int __init of_numa_parse_memory_nodes(void)
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
index 61fc349..dafb4cd 100644
--- a/drivers/pci/host/pci-hyperv.c
+++ b/drivers/pci/host/pci-hyperv.c
@@ -72,6 +72,7 @@
PCI_PROTOCOL_VERSION_CURRENT = PCI_PROTOCOL_VERSION_1_1
};
+#define CPU_AFFINITY_ALL -1ULL
#define PCI_CONFIG_MMIO_LENGTH 0x2000
#define CFG_PAGE_OFFSET 0x1000
#define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
@@ -868,7 +869,7 @@
hv_int_desc_free(hpdev, int_desc);
}
- int_desc = kzalloc(sizeof(*int_desc), GFP_KERNEL);
+ int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC);
if (!int_desc)
goto drop_reference;
@@ -889,9 +890,13 @@
* processors because Hyper-V only supports 64 in a guest.
*/
affinity = irq_data_get_affinity_mask(data);
- for_each_cpu_and(cpu, affinity, cpu_online_mask) {
- int_pkt->int_desc.cpu_mask |=
- (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+ if (cpumask_weight(affinity) >= 32) {
+ int_pkt->int_desc.cpu_mask = CPU_AFFINITY_ALL;
+ } else {
+ for_each_cpu_and(cpu, affinity, cpu_online_mask) {
+ int_pkt->int_desc.cpu_mask |=
+ (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+ }
}
ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index bcd10c7..1b07865 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -974,15 +974,19 @@
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
enum pci_mmap_api mmap_api)
{
- unsigned long nr, start, size, pci_start;
+ unsigned long nr, start, size;
+ resource_size_t pci_start = 0, pci_end;
if (pci_resource_len(pdev, resno) == 0)
return 0;
nr = vma_pages(vma);
start = vma->vm_pgoff;
size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
- pci_start = (mmap_api == PCI_MMAP_PROCFS) ?
- pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
+ if (mmap_api == PCI_MMAP_PROCFS) {
+ pci_resource_to_user(pdev, resno, &pdev->resource[resno],
+ &pci_start, &pci_end);
+ pci_start >>= PAGE_SHIFT;
+ }
if (start >= pci_start && start < pci_start + size &&
start + nr <= pci_start + size)
return 1;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 6922964..579c494 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1782,8 +1782,8 @@
}
}
if (!list_empty(&pci_pme_list))
- schedule_delayed_work(&pci_pme_work,
- msecs_to_jiffies(PME_TIMEOUT));
+ queue_delayed_work(system_freezable_wq, &pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
mutex_unlock(&pci_pme_list_mutex);
}
@@ -1848,8 +1848,9 @@
mutex_lock(&pci_pme_list_mutex);
list_add(&pme_dev->list, &pci_pme_list);
if (list_is_singular(&pci_pme_list))
- schedule_delayed_work(&pci_pme_work,
- msecs_to_jiffies(PME_TIMEOUT));
+ queue_delayed_work(system_freezable_wq,
+ &pci_pme_work,
+ msecs_to_jiffies(PME_TIMEOUT));
mutex_unlock(&pci_pme_list_mutex);
} else {
mutex_lock(&pci_pme_list_mutex);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 2408abe..66c8863 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -231,24 +231,33 @@
{
struct pci_dev *dev = PDE_DATA(file_inode(file));
struct pci_filp_private *fpriv = file->private_data;
- int i, ret, write_combine;
+ int i, ret, write_combine = 0, res_bit;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+ if (fpriv->mmap_state == pci_mmap_io)
+ res_bit = IORESOURCE_IO;
+ else
+ res_bit = IORESOURCE_MEM;
+
/* Make sure the caller is mapping a real resource for this device */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
+ if (dev->resource[i].flags & res_bit &&
+ pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
break;
}
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
- if (fpriv->mmap_state == pci_mmap_mem)
- write_combine = fpriv->write_combine;
- else
- write_combine = 0;
+ if (fpriv->mmap_state == pci_mmap_mem &&
+ fpriv->write_combine) {
+ if (dev->resource[i].flags & IORESOURCE_PREFETCH)
+ write_combine = 1;
+ else
+ return -EINVAL;
+ }
ret = pci_mmap_page_range(dev, vma,
fpriv->mmap_state, write_combine);
if (ret < 0)
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 2a1367e..9520166 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -17,6 +17,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
@@ -31,7 +32,7 @@
#include <linux/reboot.h>
#include <linux/pm.h>
#include <linux/log2.h>
-
+#include <linux/irq.h>
#include "../core.h"
#include "../pinconf.h"
#include "pinctrl-msm.h"
@@ -749,6 +750,91 @@
.irq_set_wake = msm_gpio_irq_set_wake,
};
+static void msm_dirconn_irq_mask(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_mask)
+ parent_data->chip->irq_mask(parent_data);
+}
+
+static void msm_dirconn_irq_unmask(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_unmask)
+ parent_data->chip->irq_unmask(parent_data);
+}
+
+static void msm_dirconn_irq_ack(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_ack)
+ parent_data->chip->irq_ack(parent_data);
+}
+
+static void msm_dirconn_irq_eoi(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_eoi)
+ parent_data->chip->irq_eoi(parent_data);
+}
+
+static int msm_dirconn_irq_set_affinity(struct irq_data *d,
+ const struct cpumask *maskval, bool force)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_set_affinity)
+ return parent_data->chip->irq_set_affinity(parent_data,
+ maskval, force);
+ return 0;
+}
+
+static int msm_dirconn_irq_set_vcpu_affinity(struct irq_data *d,
+ void *vcpu_info)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_set_vcpu_affinity)
+ return parent_data->chip->irq_set_vcpu_affinity(parent_data,
+ vcpu_info);
+ return 0;
+}
+
+static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+
+ if (parent_data->chip->irq_set_type)
+ return parent_data->chip->irq_set_type(parent_data, type);
+
+ return 0;
+}
+
+static struct irq_chip msm_dirconn_irq_chip = {
+ .name = "msmgpio-dc",
+ .irq_mask = msm_dirconn_irq_mask,
+ .irq_unmask = msm_dirconn_irq_unmask,
+ .irq_eoi = msm_dirconn_irq_eoi,
+ .irq_ack = msm_dirconn_irq_ack,
+ .irq_set_type = msm_dirconn_irq_set_type,
+ .irq_set_affinity = msm_dirconn_irq_set_affinity,
+ .irq_set_vcpu_affinity = msm_dirconn_irq_set_vcpu_affinity,
+ .flags = IRQCHIP_SKIP_SET_WAKE
+ | IRQCHIP_MASK_ON_SUSPEND
+ | IRQCHIP_SET_TYPE_MASKED,
+};
+
static void msm_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -783,6 +869,55 @@
chained_irq_exit(chip, desc);
}
+static void msm_gpio_dirconn_handler(struct irq_desc *desc)
+{
+ struct irq_data *irqd = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+ generic_handle_irq(irqd->irq);
+ chained_irq_exit(chip, desc);
+}
+
+static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl)
+{
+ struct device_node *parent_node;
+ struct irq_domain *parent_domain;
+ struct irq_fwspec fwspec;
+ unsigned int i;
+
+ parent_node = of_irq_find_parent(pctrl->dev->of_node);
+
+ if (!parent_node)
+ return;
+
+ parent_domain = irq_find_host(parent_node);
+ if (!parent_domain)
+ return;
+
+ fwspec.fwnode = parent_domain->fwnode;
+ for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+ const struct msm_dir_conn *dirconn = &pctrl->soc->dir_conn[i];
+ unsigned int parent_irq;
+ int irq;
+
+ fwspec.param[0] = 0; /* SPI */
+ fwspec.param[1] = dirconn->hwirq;
+ fwspec.param[2] = IRQ_TYPE_NONE;
+ fwspec.param_count = 3;
+ parent_irq = irq_create_fwspec_mapping(&fwspec);
+
+ irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio);
+
+ irq_set_parent(irq, parent_irq);
+ irq_set_chip(irq, &msm_dirconn_irq_chip);
+ irq_set_chip_data(irq, irq_get_irq_data(parent_irq));
+ __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
+ false, NULL);
+ irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+ }
+}
+
static int msm_gpio_init(struct msm_pinctrl *pctrl)
{
struct gpio_chip *chip;
@@ -827,6 +962,7 @@
gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq,
msm_gpio_irq_handler);
+ msm_gpio_setup_dir_connects(pctrl);
return 0;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index e986fda..0e223e0 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -95,6 +95,16 @@
unsigned intr_polarity_bit:5;
unsigned intr_detection_bit:5;
unsigned intr_detection_width:5;
+}
+
+/**
+ * struct msm_dir_conn - Direct GPIO connect configuration
+ * @gpio: GPIO pin number
+ * @hwirq: The GIC interrupt that the pin is connected to
+ */;
+struct msm_dir_conn {
+ unsigned int gpio;
+ irq_hw_number_t hwirq;
};
/**
@@ -106,6 +116,8 @@
* @groups: An array describing all pin groups the pin SoC supports.
* @ngroups: The numbmer of entries in @groups.
* @ngpio: The number of pingroups the driver should expose as GPIOs.
+ * @dir_conn: An array describing all the pins directly connected to GIC.
+ * @ndirconns: The number of pins directly connected to GIC
*/
struct msm_pinctrl_soc_data {
const struct pinctrl_pin_desc *pins;
@@ -115,6 +127,8 @@
const struct msm_pingroup *groups;
unsigned ngroups;
unsigned ngpios;
+ const struct msm_dir_conn *dir_conn;
+ unsigned int n_dir_conns;
};
int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index 30c31a8..7d125eb 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -2377,6 +2377,84 @@
UFS_RESET(ufs_reset, 0x99f000),
};
+static const struct msm_dir_conn sdm845_dir_conn[] = {
+ {1, 510},
+ {3, 511},
+ {5, 512},
+ {10, 513},
+ {11, 514},
+ {20, 515},
+ {22, 516},
+ {24, 517},
+ {26, 518},
+ {30, 519},
+ {31, 639},
+ {32, 521},
+ {34, 522},
+ {36, 523},
+ {37, 524},
+ {38, 525},
+ {39, 526},
+ {40, 527},
+ {41, 637},
+ {43, 529},
+ {44, 530},
+ {46, 531},
+ {48, 532},
+ {49, 640},
+ {52, 534},
+ {53, 535},
+ {54, 536},
+ {56, 537},
+ {57, 538},
+ {58, 539},
+ {59, 540},
+ {60, 541},
+ {61, 542},
+ {62, 543},
+ {63, 544},
+ {64, 545},
+ {66, 546},
+ {68, 547},
+ {71, 548},
+ {73, 549},
+ {77, 550},
+ {78, 551},
+ {79, 552},
+ {80, 553},
+ {84, 554},
+ {85, 555},
+ {86, 556},
+ {88, 557},
+ {89, 638},
+ {91, 559},
+ {92, 560},
+ {95, 561},
+ {96, 562},
+ {97, 563},
+ {101, 564},
+ {103, 565},
+ {104, 566},
+ {115, 570},
+ {116, 571},
+ {117, 572},
+ {118, 573},
+ {119, 609},
+ {120, 610},
+ {121, 611},
+ {122, 612},
+ {123, 613},
+ {124, 614},
+ {125, 615},
+ {127, 617},
+ {128, 618},
+ {129, 619},
+ {130, 620},
+ {132, 621},
+ {133, 622},
+ {145, 623},
+};
+
static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
.pins = sdm845_pins,
.npins = ARRAY_SIZE(sdm845_pins),
@@ -2385,6 +2463,8 @@
.groups = sdm845_groups,
.ngroups = ARRAY_SIZE(sdm845_groups),
.ngpios = 150,
+ .dir_conn = sdm845_dir_conn,
+ .n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
};
static int sdm845_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 4672233..bcd602c 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -80,6 +80,7 @@
u32 apps_to_ipa_hdl, ipa_to_apps_hdl; /* get handler from ipa */
static struct mutex ipa_to_apps_pipe_handle_guard;
+static struct mutex add_mux_channel_lock;
static int wwan_add_ul_flt_rule_to_ipa(void);
static int wwan_del_ul_flt_rule_to_ipa(void);
static void ipa_wwan_msg_free_cb(void*, u32, u32);
@@ -1527,9 +1528,11 @@
rmnet_mux_val.mux_id);
return rc;
}
+ mutex_lock(&add_mux_channel_lock);
if (rmnet_index >= MAX_NUM_OF_MUX_CHANNEL) {
IPAWANERR("Exceed mux_channel limit(%d)\n",
rmnet_index);
+ mutex_unlock(&add_mux_channel_lock);
return -EFAULT;
}
IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
@@ -1558,6 +1561,7 @@
IPAWANERR("device %s reg IPA failed\n",
extend_ioctl_data.u.
rmnet_mux_val.vchannel_name);
+ mutex_unlock(&add_mux_channel_lock);
return -ENODEV;
}
mux_channel[rmnet_index].mux_channel_set = true;
@@ -1570,6 +1574,7 @@
mux_channel[rmnet_index].ul_flt_reg = false;
}
rmnet_index++;
+ mutex_unlock(&add_mux_channel_lock);
break;
case RMNET_IOCTL_SET_EGRESS_DATA_FORMAT:
IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT\n");
@@ -3084,6 +3089,7 @@
atomic_set(&is_ssr, 0);
mutex_init(&ipa_to_apps_pipe_handle_guard);
+ mutex_init(&add_mux_channel_lock);
ipa_to_apps_hdl = -1;
ipa_qmi_init();
@@ -3103,6 +3109,7 @@
ipa_qmi_cleanup();
mutex_destroy(&ipa_to_apps_pipe_handle_guard);
+ mutex_destroy(&add_mux_channel_lock);
ret = subsys_notif_unregister_notifier(subsys_notify_handle,
&ssr_notifier);
if (ret)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index bf13ac5..04d807f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -3659,7 +3659,6 @@
struct ipa_mem_buffer mem_info = {0};
static int total_cnt;
- IPADBG("\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
IPAERR("bad parm 0x%x\n", clnt_hdl);
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index a15bd04..b198348 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -141,6 +141,7 @@
u32 apps_to_ipa3_hdl;
u32 ipa3_to_apps_hdl;
struct mutex pipe_handle_guard;
+ struct mutex add_mux_channel_lock;
};
static struct rmnet_ipa3_context *rmnet_ipa3_ctx;
@@ -1636,10 +1637,13 @@
rmnet_mux_val.mux_id);
return rc;
}
+ mutex_lock(&rmnet_ipa3_ctx->add_mux_channel_lock);
if (rmnet_ipa3_ctx->rmnet_index
>= MAX_NUM_OF_MUX_CHANNEL) {
IPAWANERR("Exceed mux_channel limit(%d)\n",
rmnet_ipa3_ctx->rmnet_index);
+ mutex_unlock(&rmnet_ipa3_ctx->
+ add_mux_channel_lock);
return -EFAULT;
}
IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
@@ -1673,6 +1677,8 @@
IPAWANERR("device %s reg IPA failed\n",
extend_ioctl_data.u.
rmnet_mux_val.vchannel_name);
+ mutex_unlock(&rmnet_ipa3_ctx->
+ add_mux_channel_lock);
return -ENODEV;
}
mux_channel[rmnet_index].mux_channel_set = true;
@@ -1685,6 +1691,7 @@
mux_channel[rmnet_index].ul_flt_reg = false;
}
rmnet_ipa3_ctx->rmnet_index++;
+ mutex_unlock(&rmnet_ipa3_ctx->add_mux_channel_lock);
break;
case RMNET_IOCTL_SET_EGRESS_DATA_FORMAT:
rc = handle3_egress_format(dev, &extend_ioctl_data);
@@ -3204,6 +3211,7 @@
atomic_set(&rmnet_ipa3_ctx->is_ssr, 0);
mutex_init(&rmnet_ipa3_ctx->pipe_handle_guard);
+ mutex_init(&rmnet_ipa3_ctx->add_mux_channel_lock);
rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1;
rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1;
@@ -3222,8 +3230,10 @@
static void __exit ipa3_wwan_cleanup(void)
{
int ret;
+
ipa3_qmi_cleanup();
mutex_destroy(&rmnet_ipa3_ctx->pipe_handle_guard);
+ mutex_destroy(&rmnet_ipa3_ctx->add_mux_channel_lock);
ret = subsys_notif_unregister_notifier(
rmnet_ipa3_ctx->subsys_notify_handle, &ipa3_ssr_notifier);
if (ret)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index ad627fb..7ad650e 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2980,7 +2980,8 @@
goto out2;
if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ !(rdev->desc->ops->get_voltage ||
+ rdev->desc->ops->get_voltage_sel))) {
int current_supply_uV;
int selector;
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index cf7c35d..deb0ce5 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -86,7 +86,7 @@
*/
#define CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT 32
#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 16
-#define CPRH_SDM845_KBSS_FUSE_COMBO_COUNT 16
+#define CPRH_SDM845_KBSS_FUSE_COMBO_COUNT 24
/*
* Constants which define the name of each fuse corner.
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index 3314bf2..dfa8d50 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -520,7 +520,7 @@
RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(0), 400),
RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
- RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(1), 400),
{
.name = "LDO_REG3",
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index d2c3d7c..5ca6d21 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -311,8 +311,7 @@
/* Enable setting output voltage by I2C */
regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
- TPS65023_REG_CTRL2_CORE_ADJ,
- TPS65023_REG_CTRL2_CORE_ADJ);
+ TPS65023_REG_CTRL2_CORE_ADJ, 0);
return 0;
}
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
index c4fe95a..904422f 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.c
@@ -1169,6 +1169,7 @@
cmd = list_first_entry_or_null(&vscsi->free_cmd,
struct ibmvscsis_cmd, list);
if (cmd) {
+ cmd->flags &= ~(DELAY_SEND);
list_del(&cmd->list);
cmd->iue = iue;
cmd->type = UNSET_TYPE;
@@ -1748,45 +1749,79 @@
static void ibmvscsis_send_messages(struct scsi_info *vscsi)
{
u64 msg_hi = 0;
- /* note do not attmempt to access the IU_data_ptr with this pointer
+ /* note do not attempt to access the IU_data_ptr with this pointer
* it is not valid
*/
struct viosrp_crq *crq = (struct viosrp_crq *)&msg_hi;
struct ibmvscsis_cmd *cmd, *nxt;
struct iu_entry *iue;
long rc = ADAPT_SUCCESS;
+ bool retry = false;
if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
- list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) {
- iue = cmd->iue;
+ do {
+ retry = false;
+ list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp,
+ list) {
+ /*
+ * Check to make sure abort cmd gets processed
+ * prior to the abort tmr cmd
+ */
+ if (cmd->flags & DELAY_SEND)
+ continue;
- crq->valid = VALID_CMD_RESP_EL;
- crq->format = cmd->rsp.format;
+ if (cmd->abort_cmd) {
+ retry = true;
+ cmd->abort_cmd->flags &= ~(DELAY_SEND);
+ }
- if (cmd->flags & CMD_FAST_FAIL)
- crq->status = VIOSRP_ADAPTER_FAIL;
+ /*
+ * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and
+ * the case where LIO issued a
+ * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST
+ * case then we dont send a response, since it
+ * was already done.
+ */
+ if (cmd->se_cmd.transport_state & CMD_T_ABORTED &&
+ !(cmd->se_cmd.transport_state & CMD_T_TAS)) {
+ list_del(&cmd->list);
+ ibmvscsis_free_cmd_resources(vscsi,
+ cmd);
+ } else {
+ iue = cmd->iue;
- crq->IU_length = cpu_to_be16(cmd->rsp.len);
+ crq->valid = VALID_CMD_RESP_EL;
+ crq->format = cmd->rsp.format;
- rc = h_send_crq(vscsi->dma_dev->unit_address,
- be64_to_cpu(msg_hi),
- be64_to_cpu(cmd->rsp.tag));
+ if (cmd->flags & CMD_FAST_FAIL)
+ crq->status = VIOSRP_ADAPTER_FAIL;
- pr_debug("send_messages: cmd %p, tag 0x%llx, rc %ld\n",
- cmd, be64_to_cpu(cmd->rsp.tag), rc);
+ crq->IU_length = cpu_to_be16(cmd->rsp.len);
- /* if all ok free up the command element resources */
- if (rc == H_SUCCESS) {
- /* some movement has occurred */
- vscsi->rsp_q_timer.timer_pops = 0;
- list_del(&cmd->list);
+ rc = h_send_crq(vscsi->dma_dev->unit_address,
+ be64_to_cpu(msg_hi),
+ be64_to_cpu(cmd->rsp.tag));
- ibmvscsis_free_cmd_resources(vscsi, cmd);
- } else {
- srp_snd_msg_failed(vscsi, rc);
- break;
+ pr_debug("send_messages: cmd %p, tag 0x%llx, rc %ld\n",
+ cmd, be64_to_cpu(cmd->rsp.tag), rc);
+
+ /* if all ok free up the command
+ * element resources
+ */
+ if (rc == H_SUCCESS) {
+ /* some movement has occurred */
+ vscsi->rsp_q_timer.timer_pops = 0;
+ list_del(&cmd->list);
+
+ ibmvscsis_free_cmd_resources(vscsi,
+ cmd);
+ } else {
+ srp_snd_msg_failed(vscsi, rc);
+ break;
+ }
+ }
}
- }
+ } while (retry);
if (!rc) {
/*
@@ -2707,6 +2742,7 @@
for (i = 0, cmd = (struct ibmvscsis_cmd *)vscsi->cmd_pool; i < num;
i++, cmd++) {
+ cmd->abort_cmd = NULL;
cmd->adapter = vscsi;
INIT_WORK(&cmd->work, ibmvscsis_scheduler);
list_add_tail(&cmd->list, &vscsi->free_cmd);
@@ -3578,9 +3614,20 @@
{
struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
se_cmd);
+ struct scsi_info *vscsi = cmd->adapter;
struct iu_entry *iue = cmd->iue;
int rc;
+ /*
+ * If CLIENT_FAILED OR RESPONSE_Q_DOWN, then just return success
+ * since LIO can't do anything about it, and we dont want to
+ * attempt an srp_transfer_data.
+ */
+ if ((vscsi->flags & (CLIENT_FAILED | RESPONSE_Q_DOWN))) {
+ pr_err("write_pending failed since: %d\n", vscsi->flags);
+ return 0;
+ }
+
rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma,
1, 1);
if (rc) {
@@ -3659,11 +3706,28 @@
struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
se_cmd);
struct scsi_info *vscsi = cmd->adapter;
+ struct ibmvscsis_cmd *cmd_itr;
+ struct iu_entry *iue = iue = cmd->iue;
+ struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
+ u64 tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
uint len;
pr_debug("queue_tm_rsp %p, status %d\n",
se_cmd, (int)se_cmd->se_tmr_req->response);
+ if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK &&
+ cmd->se_cmd.se_tmr_req->response == TMR_TASK_DOES_NOT_EXIST) {
+ spin_lock_bh(&vscsi->intr_lock);
+ list_for_each_entry(cmd_itr, &vscsi->active_q, list) {
+ if (tag_to_abort == cmd_itr->se_cmd.tag) {
+ cmd_itr->abort_cmd = cmd;
+ cmd->flags |= DELAY_SEND;
+ break;
+ }
+ }
+ spin_unlock_bh(&vscsi->intr_lock);
+ }
+
srp_build_response(vscsi, cmd, &len);
cmd->rsp.format = SRP_FORMAT;
cmd->rsp.len = len;
@@ -3671,8 +3735,8 @@
static void ibmvscsis_aborted_task(struct se_cmd *se_cmd)
{
- /* TBD: What (if anything) should we do here? */
- pr_debug("ibmvscsis_aborted_task %p\n", se_cmd);
+ pr_debug("ibmvscsis_aborted_task %p task_tag: %llu\n",
+ se_cmd, se_cmd->tag);
}
static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
diff --git a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
index 98b0ca7..f5683af 100644
--- a/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
+++ b/drivers/scsi/ibmvscsi_tgt/ibmvscsi_tgt.h
@@ -167,10 +167,12 @@
struct iu_rsp rsp;
struct work_struct work;
struct scsi_info *adapter;
+ struct ibmvscsis_cmd *abort_cmd;
/* Sense buffer that will be mapped into outgoing status */
unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
u64 init_time;
#define CMD_FAST_FAIL BIT(0)
+#define DELAY_SEND BIT(1)
u32 flags;
char type;
};
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2f6cd95..6418c11 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1523,6 +1523,7 @@
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
out:
+ hba->ufs_stats.clk_hold.ts = ktime_get();
return rc;
}
EXPORT_SYMBOL_GPL(ufshcd_hold);
@@ -1627,6 +1628,7 @@
hba->clk_gating.state = REQ_CLKS_OFF;
trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state);
+ hba->ufs_stats.clk_rel.ts = ktime_get();
hrtimer_start(&hba->clk_gating.gate_hrtimer,
ms_to_ktime(hba->clk_gating.delay_ms),
@@ -2073,8 +2075,10 @@
/* Exit from hibern8 */
if (ufshcd_is_link_hibern8(hba)) {
+ hba->ufs_stats.clk_hold.ctx = H8_EXIT_WORK;
ufshcd_hold(hba, false);
ret = ufshcd_uic_hibern8_exit(hba);
+ hba->ufs_stats.clk_rel.ctx = H8_EXIT_WORK;
ufshcd_release(hba, false);
if (!ret) {
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -2500,6 +2504,7 @@
int ret;
unsigned long flags;
+ hba->ufs_stats.clk_hold.ctx = UIC_CMD_SEND;
ufshcd_hold_all(hba);
mutex_lock(&hba->uic_cmd_mutex);
ufshcd_add_delay_before_dme_cmd(hba);
@@ -2513,6 +2518,7 @@
ufshcd_save_tstamp_of_last_dme_cmd(hba);
mutex_unlock(&hba->uic_cmd_mutex);
ufshcd_release_all(hba);
+ hba->ufs_stats.clk_rel.ctx = UIC_CMD_SEND;
ufsdbg_error_inject_dispatcher(hba,
ERR_INJECT_UIC, 0, &ret);
@@ -2999,6 +3005,7 @@
goto out;
}
+ hba->ufs_stats.clk_hold.ctx = QUEUE_CMD;
err = ufshcd_hold(hba, true);
if (err) {
err = SCSI_MLQUEUE_HOST_BUSY;
@@ -3013,6 +3020,7 @@
if (err) {
clear_bit_unlock(tag, &hba->lrb_in_use);
err = SCSI_MLQUEUE_HOST_BUSY;
+ hba->ufs_stats.clk_rel.ctx = QUEUE_CMD;
ufshcd_release(hba, true);
goto out;
}
@@ -4392,8 +4400,10 @@
uic_cmd.command = UIC_CMD_DME_SET;
uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
uic_cmd.argument3 = mode;
+ hba->ufs_stats.clk_hold.ctx = PWRCTL_CMD_SEND;
ufshcd_hold_all(hba);
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+ hba->ufs_stats.clk_rel.ctx = PWRCTL_CMD_SEND;
ufshcd_release_all(hba);
out:
return ret;
@@ -5580,6 +5590,7 @@
update_req_stats(hba, lrbp);
/* Mark completed command as NULL in LRB */
lrbp->cmd = NULL;
+ hba->ufs_stats.clk_rel.ctx = XFR_REQ_COMPL;
__ufshcd_release(hba, false);
__ufshcd_hibern8_release(hba, false);
if (cmd->request) {
@@ -6101,6 +6112,7 @@
if (unlikely((hba->clk_gating.state != CLKS_ON) &&
ufshcd_is_auto_hibern8_supported(hba))) {
spin_unlock_irqrestore(hba->host->host_lock, flags);
+ hba->ufs_stats.clk_hold.ctx = ERR_HNDLR_WORK;
ufshcd_hold(hba, false);
spin_lock_irqsave(hba->host->host_lock, flags);
clks_enabled = true;
@@ -6245,8 +6257,10 @@
hba->silence_err_logs = false;
- if (clks_enabled)
+ if (clks_enabled) {
__ufshcd_release(hba, false);
+ hba->ufs_stats.clk_rel.ctx = ERR_HNDLR_WORK;
+ }
out:
ufshcd_clear_eh_in_progress(hba);
spin_unlock_irqrestore(hba->host->host_lock, flags);
@@ -6482,7 +6496,8 @@
spin_lock(hba->host->host_lock);
intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
-
+ hba->ufs_stats.last_intr_status = intr_status;
+ hba->ufs_stats.last_intr_ts = ktime_get();
/*
* There could be max of hba->nutrs reqs in flight and in worst case
* if the reqs get finished 1 by 1 after the interrupt status is
@@ -6561,6 +6576,7 @@
* the maximum wait time is bounded by %TM_CMD_TIMEOUT.
*/
wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot));
+ hba->ufs_stats.clk_hold.ctx = TM_CMD_SEND;
ufshcd_hold_all(hba);
spin_lock_irqsave(host->host_lock, flags);
@@ -6618,6 +6634,7 @@
clear_bit(free_slot, &hba->tm_condition);
ufshcd_put_tm_slot(hba, free_slot);
wake_up(&hba->tm_tag_wq);
+ hba->ufs_stats.clk_rel.ctx = TM_CMD_SEND;
ufshcd_release_all(hba);
return err;
@@ -9635,6 +9652,7 @@
int ret = 0;
/* let's not get into low power until clock scaling is completed */
+ hba->ufs_stats.clk_hold.ctx = CLK_SCALE_WORK;
ufshcd_hold_all(hba);
ret = ufshcd_clock_scaling_prepare(hba);
@@ -9698,6 +9716,7 @@
clk_scaling_unprepare:
ufshcd_clock_scaling_unprepare(hba);
out:
+ hba->ufs_stats.clk_rel.ctx = CLK_SCALE_WORK;
ufshcd_release_all(hba);
return ret;
}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 6966aac..77ccc39 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -584,6 +584,22 @@
};
#endif
+enum ufshcd_ctx {
+ QUEUE_CMD,
+ ERR_HNDLR_WORK,
+ H8_EXIT_WORK,
+ UIC_CMD_SEND,
+ PWRCTL_CMD_SEND,
+ TM_CMD_SEND,
+ XFR_REQ_COMPL,
+ CLK_SCALE_WORK,
+};
+
+struct ufshcd_clk_ctx {
+ ktime_t ts;
+ enum ufshcd_ctx ctx;
+};
+
/**
* struct ufs_stats - keeps usage/err statistics
* @enabled: enable tag stats for debugfs
@@ -612,6 +628,10 @@
int query_stats_arr[UPIU_QUERY_OPCODE_MAX][MAX_QUERY_IDN];
#endif
+ u32 last_intr_status;
+ ktime_t last_intr_ts;
+ struct ufshcd_clk_ctx clk_hold;
+ struct ufshcd_clk_ctx clk_rel;
u32 hibern8_exit_cnt;
ktime_t last_hibern8_exit_tstamp;
struct ufs_uic_err_reg_hist pa_err;
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index f7f0269..a72cb17 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -223,6 +223,7 @@
/* make sure autosuspend is not called until ADSP comes up*/
pm_runtime_get_noresume(dev->dev);
dev->state = MSM_CTRL_DOWN;
+ dev->qmi.deferred_resp = false;
msm_slim_sps_exit(dev, false);
ngd_dom_down(dev);
mutex_unlock(&dev->tx_lock);
@@ -2019,19 +2020,18 @@
if (!pm_runtime_enabled(dev) ||
(!pm_runtime_suspended(dev) &&
cdev->state == MSM_CTRL_IDLE)) {
+ cdev->qmi.deferred_resp = true;
ret = ngd_slim_runtime_suspend(dev);
/*
* If runtime-PM still thinks it's active, then make sure its
* status is in sync with HW status.
- * Since this suspend calls QMI api, it results in holding a
- * wakelock. That results in failure of first suspend.
- * Subsequent suspend should not call low-power transition
- * again since the HW is already in suspended state.
*/
if (!ret) {
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
+ } else {
+ cdev->qmi.deferred_resp = false;
}
}
if (ret == -EBUSY) {
@@ -2053,13 +2053,29 @@
{
struct platform_device *pdev = to_platform_device(dev);
struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ /*
+ * If deferred response was requested for power-off and it failed,
+ * mark runtime-pm status as active to be consistent
+ * with HW status
+ */
+ if (cdev->qmi.deferred_resp) {
+ ret = msm_slim_qmi_deferred_status_req(cdev);
+ if (ret) {
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ }
+ cdev->qmi.deferred_resp = false;
+ }
/*
* Rely on runtime-PM to call resume in case it is enabled.
* Even if it's not enabled, rely on 1st client transaction to do
* clock/power on
*/
SLIM_INFO(cdev, "system resume\n");
- return 0;
+ return ret;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index e7d3381..ef10e64 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -1224,12 +1224,16 @@
#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ 0x0022
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP 0x0022
+#define SLIMBUS_QMI_DEFERRED_STATUS_REQ 0x0023
+#define SLIMBUS_QMI_DEFERRED_STATUS_RESP 0x0023
-#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_DEFERRED_STATUS_REQ_MSG_MAX_MSG_LEN 0
+#define SLIMBUS_QMI_DEFERRED_STATUS_RESP_STAT_MSG_MAX_MSG_LEN 7
enum slimbus_mode_enum_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
@@ -1247,6 +1251,13 @@
SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
+enum slimbus_resp_enum_type_v01 {
+ SLIMBUS_RESP_ENUM_TYPE_MIN_VAL_V01 = INT_MIN,
+ SLIMBUS_RESP_SYNCHRONOUS_V01 = 1,
+ SLIMBUS_RESP_DEFERRED_V01 = 2,
+ SLIMBUS_RESP_ENUM_TYPE_MAX_VAL_V01 = INT_MAX,
+};
+
struct slimbus_select_inst_req_msg_v01 {
/* Mandatory */
/* Hardware Instance Selection */
@@ -1269,6 +1280,12 @@
/* Mandatory */
/* Power Request Operation */
enum slimbus_pm_enum_type_v01 pm_req;
+
+ /* Optional */
+ /* Optional Deferred Response type Operation */
+ /* Must be set to true if type is being passed */
+ uint8_t resp_type_valid;
+ enum slimbus_resp_enum_type_v01 resp_type;
};
struct slimbus_power_resp_msg_v01 {
@@ -1283,6 +1300,9 @@
struct qmi_response_type_v01 resp;
};
+struct slimbus_deferred_status_resp {
+ struct qmi_response_type_v01 resp;
+};
static struct elem_info slimbus_select_inst_req_msg_v01_ei[] = {
{
@@ -1359,6 +1379,24 @@
.ei_array = NULL,
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct slimbus_power_req_msg_v01,
+ resp_type_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum slimbus_resp_enum_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct slimbus_power_req_msg_v01,
+ resp_type),
+ },
+ {
.data_type = QMI_EOTI,
.elem_len = 0,
.elem_size = 0,
@@ -1411,6 +1449,22 @@
},
};
+static struct elem_info slimbus_deferred_status_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct slimbus_deferred_status_resp,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ },
+};
static void msm_slim_qmi_recv_msg(struct kthread_work *work)
{
int rc;
@@ -1488,32 +1542,56 @@
return 0;
}
+static void slim_qmi_resp_cb(struct qmi_handle *handle, unsigned int msg_id,
+ void *msg, void *resp_cb_data, int stat)
+{
+ struct slimbus_power_resp_msg_v01 *resp = msg;
+ struct msm_slim_ctrl *dev = resp_cb_data;
+
+ if (msg_id != SLIMBUS_QMI_POWER_RESP_V01)
+ SLIM_WARN(dev, "incorrect msg id in qmi-resp CB:0x%x", msg_id);
+ else if (resp->resp.result != QMI_RESULT_SUCCESS_V01)
+ SLIM_ERR(dev, "%s: QMI power failed 0x%x (%s)\n", __func__,
+ resp->resp.result, get_qmi_error(&resp->resp));
+
+ complete(&dev->qmi.defer_comp);
+}
+
static int msm_slim_qmi_send_power_request(struct msm_slim_ctrl *dev,
struct slimbus_power_req_msg_v01 *req)
{
- struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
- struct msg_desc req_desc, resp_desc;
+ struct slimbus_power_resp_msg_v01 *resp =
+ (struct slimbus_power_resp_msg_v01 *)&dev->qmi.resp;
+ struct msg_desc req_desc;
+ struct msg_desc *resp_desc = &dev->qmi.resp_desc;
int rc;
req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
req_desc.max_msg_len = SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN;
req_desc.ei_array = slimbus_power_req_msg_v01_ei;
- resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
- resp_desc.max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
- resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
+ resp_desc->msg_id = SLIMBUS_QMI_POWER_RESP_V01;
+ resp_desc->max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
+ resp_desc->ei_array = slimbus_power_resp_msg_v01_ei;
- rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
- &resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
- if (rc < 0) {
+ if (dev->qmi.deferred_resp)
+ rc = qmi_send_req_nowait(dev->qmi.handle, &req_desc, req,
+ sizeof(*req), resp_desc, resp,
+ sizeof(*resp), slim_qmi_resp_cb, dev);
+ else
+ rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req,
+ sizeof(*req), resp_desc, resp,
+ sizeof(*resp), SLIM_QMI_RESP_TOUT);
+ if (rc < 0)
SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
+
+ if (rc < 0 || dev->qmi.deferred_resp)
return rc;
- }
/* Check the response */
- if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
- resp.resp.result, get_qmi_error(&resp.resp));
+ resp->resp.result, get_qmi_error(&resp->resp));
return -EREMOTEIO;
}
@@ -1527,6 +1605,7 @@
struct slimbus_select_inst_req_msg_v01 req;
kthread_init_worker(&dev->qmi.kworker);
+ init_completion(&dev->qmi.defer_comp);
dev->qmi.task = kthread_run(kthread_worker_fn,
&dev->qmi.kworker, "msm_slim_qmi_clnt%d", dev->ctrl.nr);
@@ -1604,6 +1683,13 @@
else
req.pm_req = SLIMBUS_PM_INACTIVE_V01;
+ if (dev->qmi.deferred_resp) {
+ req.resp_type = SLIMBUS_RESP_DEFERRED_V01;
+ req.resp_type_valid = 1;
+ } else {
+ req.resp_type_valid = 0;
+ }
+
return msm_slim_qmi_send_power_request(dev, &req);
}
@@ -1635,3 +1721,46 @@
}
return 0;
}
+
+int msm_slim_qmi_deferred_status_req(struct msm_slim_ctrl *dev)
+{
+ struct slimbus_deferred_status_resp resp = { { 0, 0 } };
+ struct msg_desc req_desc, resp_desc;
+ int rc;
+
+ req_desc.msg_id = SLIMBUS_QMI_DEFERRED_STATUS_REQ;
+ req_desc.max_msg_len = 0;
+ req_desc.ei_array = NULL;
+
+ resp_desc.msg_id = SLIMBUS_QMI_DEFERRED_STATUS_RESP;
+ resp_desc.max_msg_len =
+ SLIMBUS_QMI_DEFERRED_STATUS_RESP_STAT_MSG_MAX_MSG_LEN;
+ resp_desc.ei_array = slimbus_deferred_status_resp_msg_v01_ei;
+
+ rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, NULL, 0,
+ &resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
+ if (rc < 0) {
+ SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
+ return rc;
+ }
+ /* Check the response */
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n",
+ __func__, resp.resp.result, get_qmi_error(&resp.resp));
+ return -EREMOTEIO;
+ }
+
+ /* wait for the deferred response */
+ rc = wait_for_completion_timeout(&dev->qmi.defer_comp, HZ);
+ if (rc == 0) {
+ SLIM_WARN(dev, "slimbus power deferred response not rcvd\n");
+ return -ETIMEDOUT;
+ }
+ /* Check what response we got in callback */
+ if (dev->qmi.resp.result != QMI_RESULT_SUCCESS_V01) {
+ SLIM_WARN(dev, "QMI power req failed in CB");
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 65b9fae..ee0f625 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -228,6 +228,10 @@
struct kthread_worker kworker;
struct completion qmi_comp;
struct notifier_block nb;
+ bool deferred_resp;
+ struct qmi_response_type_v01 resp;
+ struct msg_desc resp_desc;
+ struct completion defer_comp;
};
enum msm_slim_dom {
@@ -437,4 +441,5 @@
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
int msm_slim_qmi_check_framer_request(struct msm_slim_ctrl *dev);
+int msm_slim_qmi_deferred_status_req(struct msm_slim_ctrl *dev);
#endif
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index 93e6994..42f146d 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -154,6 +154,7 @@
void *sram_buf;
struct msm_dump_data sram_data;
uint8_t curr_list;
+ uint8_t cti_trig;
};
static bool dcc_ready(struct dcc_drvdata *drvdata)
@@ -601,7 +602,8 @@
}
/* 4. Configure trigger, data sink and function type */
- dcc_writel(drvdata, BIT(9) | ((drvdata->data_sink << 4) |
+ dcc_writel(drvdata, BIT(9) | ((drvdata->cti_trig << 8) |
+ (drvdata->data_sink << 4) |
(drvdata->func_type[list])), DCC_LL_CFG(list));
/* 5. Clears interrupt status register */
@@ -1252,6 +1254,43 @@
}
static DEVICE_ATTR(config_write, 0200, NULL, dcc_write);
+static ssize_t dcc_show_cti_trig(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->cti_trig);
+}
+
+static ssize_t dcc_store_cti_trig(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long val;
+ int ret = 0;
+ struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ if (kstrtoul(buf, 16, &val))
+ return -EINVAL;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (drvdata->enable[drvdata->curr_list]) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (val)
+ drvdata->cti_trig = 1;
+ else
+ drvdata->cti_trig = 0;
+out:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+static DEVICE_ATTR(cti_trig, 0644,
+ dcc_show_cti_trig, dcc_store_cti_trig);
+
static const struct device_attribute *dcc_attrs[] = {
&dev_attr_func_type,
&dev_attr_data_sink,
@@ -1266,6 +1305,7 @@
&dev_attr_rd_mod_wr,
&dev_attr_curr_list,
&dev_attr_config_write,
+ &dev_attr_cti_trig,
NULL,
};
diff --git a/drivers/soc/qcom/early_random.c b/drivers/soc/qcom/early_random.c
index 0c562ec..5156bc1 100644
--- a/drivers/soc/qcom/early_random.c
+++ b/drivers/soc/qcom/early_random.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016-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
@@ -12,7 +13,7 @@
*/
#include <linux/kernel.h>
-#include <linux/random.h>
+#include <linux/hw_random.h>
#include <linux/io.h>
#include <soc/qcom/scm.h>
@@ -57,7 +58,7 @@
if (!ret) {
dmac_inv_range(random_buffer, random_buffer +
RANDOM_BUFFER_SIZE);
- add_device_randomness(random_buffer, SZ_512);
+ add_hwgenerator_randomness(random_buffer, SZ_512, SZ_512 << 3);
}
}
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 69e0ebc..e3b5826 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -691,6 +691,8 @@
goto out;
}
+ memset(&ind_msg, 0, sizeof(ind_msg));
+
ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01;
ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN;
ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei;
@@ -3969,6 +3971,9 @@
data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
return -EINVAL;
+ kfree(priv->diag_reg_read_buf);
+ priv->diag_reg_read_buf = NULL;
+
reg_buf = kzalloc(data_len, GFP_KERNEL);
if (!reg_buf)
return -ENOMEM;
@@ -4002,12 +4007,13 @@
.llseek = seq_lseek,
};
+#ifdef CONFIG_ICNSS_DEBUG
static int icnss_debugfs_create(struct icnss_priv *priv)
{
int ret = 0;
struct dentry *root_dentry;
- root_dentry = debugfs_create_dir("icnss", 0);
+ root_dentry = debugfs_create_dir("icnss", NULL);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
@@ -4017,19 +4023,40 @@
priv->root_dentry = root_dentry;
- debugfs_create_file("fw_debug", 0644, root_dentry, priv,
+ debugfs_create_file("fw_debug", 0600, root_dentry, priv,
&icnss_fw_debug_fops);
- debugfs_create_file("stats", 0644, root_dentry, priv,
+ debugfs_create_file("stats", 0600, root_dentry, priv,
&icnss_stats_fops);
debugfs_create_file("reg_read", 0600, root_dentry, priv,
&icnss_regread_fops);
- debugfs_create_file("reg_write", 0644, root_dentry, priv,
+ debugfs_create_file("reg_write", 0600, root_dentry, priv,
&icnss_regwrite_fops);
out:
return ret;
}
+#else
+static int icnss_debugfs_create(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct dentry *root_dentry;
+
+ root_dentry = debugfs_create_dir("icnss", NULL);
+
+ if (IS_ERR(root_dentry)) {
+ ret = PTR_ERR(root_dentry);
+ icnss_pr_err("Unable to create debugfs %d\n", ret);
+ return ret;
+ }
+
+ priv->root_dentry = root_dentry;
+
+ debugfs_create_file("stats", 0600, root_dentry, priv,
+ &icnss_stats_fops);
+ return 0;
+}
+#endif
static void icnss_debugfs_destroy(struct icnss_priv *priv)
{
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index b9ce417..5ed66bf 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-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
@@ -19,6 +19,9 @@
#include <linux/of_address.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/scm.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0)
@@ -195,3 +198,84 @@
}
early_initcall(init_debug_lar_unlock);
#endif
+
+static int mem_dump_probe(struct platform_device *pdev)
+{
+ struct device_node *child_node;
+ const struct device_node *node = pdev->dev.of_node;
+ static dma_addr_t dump_addr;
+ static void *dump_vaddr;
+ struct msm_dump_data *dump_data;
+ struct msm_dump_entry dump_entry;
+ int ret;
+ u32 size, id;
+
+ for_each_available_child_of_node(node, child_node) {
+ ret = of_property_read_u32(child_node, "qcom,dump-size", &size);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to find size for %s\n",
+ child_node->name);
+ continue;
+ }
+
+ ret = of_property_read_u32(child_node, "qcom,dump-id", &id);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to find id for %s\n",
+ child_node->name);
+ continue;
+ }
+
+ dump_vaddr = (void *) dma_alloc_coherent(&pdev->dev, size,
+ &dump_addr, GFP_KERNEL);
+
+ if (!dump_vaddr) {
+ dev_err(&pdev->dev, "Couldn't get memory for dumping\n");
+ continue;
+ }
+
+ memset(dump_vaddr, 0x0, size);
+
+ dump_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_dump_data), GFP_KERNEL);
+ if (!dump_data) {
+ dma_free_coherent(&pdev->dev, size, dump_vaddr,
+ dump_addr);
+ continue;
+ }
+
+ dump_data->addr = dump_addr;
+ dump_data->len = size;
+ dump_entry.id = id;
+ dump_entry.addr = virt_to_phys(dump_data);
+ ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
+ if (ret) {
+ dev_err(&pdev->dev, "Data dump setup failed, id = %d\n",
+ id);
+ dma_free_coherent(&pdev->dev, size, dump_vaddr,
+ dump_addr);
+ devm_kfree(&pdev->dev, dump_data);
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id mem_dump_match_table[] = {
+ {.compatible = "qcom,mem-dump",},
+ {}
+};
+
+static struct platform_driver mem_dump_driver = {
+ .probe = mem_dump_probe,
+ .driver = {
+ .name = "msm_mem_dump",
+ .owner = THIS_MODULE,
+ .of_match_table = mem_dump_match_table,
+ },
+};
+
+static int __init mem_dump_init(void)
+{
+ return platform_driver_register(&mem_dump_driver);
+}
+
+pure_initcall(mem_dump_init);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
index bf5a526..9d0adbb 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
@@ -24,8 +24,10 @@
#define NUM_LNODES 3
#define MAX_STR_CL 50
-#define MSM_BUS_MAS_ALC 144
-#define MSM_BUS_RSC_APPS 8000
+#define MSM_BUS_MAS_ALC 144
+#define MSM_BUS_RSC_APPS 8000
+#define MSM_BUS_RSC_DISP 8001
+#define BCM_TCS_CMD_ACV_APPS 0x8
struct bus_search_type {
struct list_head link;
@@ -127,16 +129,14 @@
goto exit_bcm_add_bus_req;
}
- if (cur_dev->node_info->bcm_req_idx != -1)
- goto exit_bcm_add_bus_req;
-
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_add_bus_req;
for (i = 0; i < cur_dev->node_info->num_bcm_devs; i++) {
+ if (cur_dev->node_info->bcm_req_idx[i] != -1)
+ continue;
bcm_dev = to_msm_bus_node(cur_dev->node_info->bcm_devs[i]);
max_num_lnodes = bcm_dev->bcmdev->num_bus_devs;
-
if (!bcm_dev->num_lnodes) {
bcm_dev->lnode_list = devm_kzalloc(dev,
sizeof(struct link_node) * max_num_lnodes,
@@ -183,7 +183,7 @@
lnode->in_use = 1;
lnode->bus_dev_id = cur_dev->node_info->id;
- cur_dev->node_info->bcm_req_idx = lnode_idx;
+ cur_dev->node_info->bcm_req_idx[i] = lnode_idx;
memset(lnode->lnode_ib, 0, sizeof(uint64_t) * NUM_CTX);
memset(lnode->lnode_ab, 0, sizeof(uint64_t) * NUM_CTX);
}
@@ -483,11 +483,35 @@
return first_hop;
}
+static void bcm_update_acv_req(struct msm_bus_node_device_type *cur_rsc,
+ uint64_t max_ab, uint64_t max_ib,
+ uint64_t *vec_a, uint64_t *vec_b,
+ uint32_t *acv, int ctx)
+{
+ uint32_t acv_bmsk = 0;
+ /*
+ * Base ACV voting on current RSC until mapping is set up in commanddb
+ * that allows us to vote ACV based on master.
+ */
+
+ if (cur_rsc->node_info->id == MSM_BUS_RSC_APPS)
+ acv_bmsk = BCM_TCS_CMD_ACV_APPS;
+
+ if (max_ab == 0 && max_ib == 0)
+ *acv = *acv & ~acv_bmsk;
+ else
+ *acv = *acv | acv_bmsk;
+ *vec_a = 0;
+ *vec_b = *acv;
+}
+
static void bcm_update_bus_req(struct device *dev, int ctx)
{
struct msm_bus_node_device_type *cur_dev = NULL;
struct msm_bus_node_device_type *bcm_dev = NULL;
- int i;
+ struct msm_bus_node_device_type *cur_rsc = NULL;
+
+ int i, j;
uint64_t max_ib = 0;
uint64_t max_ab = 0;
int lnode_idx = 0;
@@ -507,7 +531,7 @@
if (!bcm_dev)
goto exit_bcm_update_bus_req;
- lnode_idx = cur_dev->node_info->bcm_req_idx;
+ lnode_idx = cur_dev->node_info->bcm_req_idx[i];
bcm_dev->lnode_list[lnode_idx].lnode_ib[ctx] =
msm_bus_div64(cur_dev->node_bw[ctx].max_ib *
(uint64_t)bcm_dev->bcmdev->width,
@@ -519,19 +543,19 @@
cur_dev->node_info->agg_params.buswidth *
cur_dev->node_info->agg_params.num_aggports);
- for (i = 0; i < bcm_dev->num_lnodes; i++) {
+ for (j = 0; j < bcm_dev->num_lnodes; j++) {
if (ctx == ACTIVE_CTX) {
max_ib = max(max_ib,
- max(bcm_dev->lnode_list[i].lnode_ib[ACTIVE_CTX],
- bcm_dev->lnode_list[i].lnode_ib[DUAL_CTX]));
+ max(bcm_dev->lnode_list[j].lnode_ib[ACTIVE_CTX],
+ bcm_dev->lnode_list[j].lnode_ib[DUAL_CTX]));
max_ab = max(max_ab,
- bcm_dev->lnode_list[i].lnode_ab[ACTIVE_CTX] +
- bcm_dev->lnode_list[i].lnode_ab[DUAL_CTX]);
+ bcm_dev->lnode_list[j].lnode_ab[ACTIVE_CTX] +
+ bcm_dev->lnode_list[j].lnode_ab[DUAL_CTX]);
} else {
max_ib = max(max_ib,
- bcm_dev->lnode_list[i].lnode_ib[ctx]);
+ bcm_dev->lnode_list[j].lnode_ib[ctx]);
max_ab = max(max_ab,
- bcm_dev->lnode_list[i].lnode_ab[ctx]);
+ bcm_dev->lnode_list[j].lnode_ab[ctx]);
}
}
bcm_dev->node_bw[ctx].max_ab = max_ab;
@@ -540,8 +564,18 @@
max_ab = msm_bus_div64(max_ab, bcm_dev->bcmdev->unit_size);
max_ib = msm_bus_div64(max_ib, bcm_dev->bcmdev->unit_size);
- bcm_dev->node_vec[ctx].vec_a = max_ab;
- bcm_dev->node_vec[ctx].vec_b = max_ib;
+ if (bcm_dev->node_info->id == MSM_BUS_BCM_ACV) {
+ cur_rsc = to_msm_bus_node(bcm_dev->node_info->
+ rsc_devs[0]);
+ bcm_update_acv_req(cur_rsc, max_ab, max_ib,
+ &bcm_dev->node_vec[ctx].vec_a,
+ &bcm_dev->node_vec[ctx].vec_b,
+ &cur_rsc->rscdev->acv[ctx], ctx);
+
+ } else {
+ bcm_dev->node_vec[ctx].vec_a = max_ab;
+ bcm_dev->node_vec[ctx].vec_b = max_ib;
+ }
}
exit_bcm_update_bus_req:
return;
@@ -551,7 +585,8 @@
{
struct msm_bus_node_device_type *cur_dev = NULL;
struct msm_bus_node_device_type *bcm_dev = NULL;
- int i;
+ struct msm_bus_node_device_type *cur_rsc = NULL;
+ int i, j;
uint64_t max_query_ib = 0;
uint64_t max_query_ab = 0;
int lnode_idx = 0;
@@ -571,7 +606,7 @@
if (!bcm_dev)
goto exit_bcm_query_bus_req;
- lnode_idx = cur_dev->node_info->bcm_req_idx;
+ lnode_idx = cur_dev->node_info->bcm_req_idx[i];
bcm_dev->lnode_list[lnode_idx].lnode_query_ib[ctx] =
msm_bus_div64(cur_dev->node_bw[ctx].max_query_ib *
(uint64_t)bcm_dev->bcmdev->width,
@@ -583,25 +618,25 @@
cur_dev->node_info->agg_params.num_aggports *
cur_dev->node_info->agg_params.buswidth);
- for (i = 0; i < bcm_dev->num_lnodes; i++) {
+ for (j = 0; j < bcm_dev->num_lnodes; j++) {
if (ctx == ACTIVE_CTX) {
max_query_ib = max(max_query_ib,
- max(bcm_dev->lnode_list[i].
+ max(bcm_dev->lnode_list[j].
lnode_query_ib[ACTIVE_CTX],
- bcm_dev->lnode_list[i].
+ bcm_dev->lnode_list[j].
lnode_query_ib[DUAL_CTX]));
max_query_ab = max(max_query_ab,
- bcm_dev->lnode_list[i].
+ bcm_dev->lnode_list[j].
lnode_query_ab[ACTIVE_CTX] +
- bcm_dev->lnode_list[i].
+ bcm_dev->lnode_list[j].
lnode_query_ab[DUAL_CTX]);
} else {
max_query_ib = max(max_query_ib,
- bcm_dev->lnode_list[i].
+ bcm_dev->lnode_list[j].
lnode_query_ib[ctx]);
max_query_ab = max(max_query_ab,
- bcm_dev->lnode_list[i].
+ bcm_dev->lnode_list[j].
lnode_query_ab[ctx]);
}
}
@@ -611,6 +646,18 @@
max_query_ib = msm_bus_div64(max_query_ib,
bcm_dev->bcmdev->unit_size);
+ if (bcm_dev->node_info->id == MSM_BUS_BCM_ACV) {
+ cur_rsc = to_msm_bus_node(bcm_dev->node_info->
+ rsc_devs[0]);
+ bcm_update_acv_req(cur_rsc, max_query_ab, max_query_ib,
+ &bcm_dev->node_vec[ctx].query_vec_a,
+ &bcm_dev->node_vec[ctx].query_vec_b,
+ &cur_rsc->rscdev->query_acv[ctx], ctx);
+ } else {
+ bcm_dev->node_vec[ctx].query_vec_a = max_query_ab;
+ bcm_dev->node_vec[ctx].query_vec_b = max_query_ib;
+ }
+
bcm_dev->node_bw[ctx].max_query_ab = max_query_ab;
bcm_dev->node_bw[ctx].max_query_ib = max_query_ib;
}
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
index c950367..458cf0d 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
@@ -418,8 +418,8 @@
commit = true;
}
tcs_cmd_gen(cur_bcm, &cmdlist_active[k],
- cur_bcm->node_bw[ACTIVE_CTX].max_query_ib,
- cur_bcm->node_bw[ACTIVE_CTX].max_query_ab,
+ cur_bcm->node_vec[ACTIVE_CTX].query_vec_a,
+ cur_bcm->node_vec[ACTIVE_CTX].query_vec_b,
commit);
k++;
}
@@ -433,26 +433,30 @@
{
int ret = 0;
int cur_vcd = 0;
+ int i = 0;
struct msm_bus_node_device_type *cur_bcm = NULL;
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_clist_add;
- cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[0]);
- cur_vcd = cur_bcm->bcmdev->clk_domain;
+ for (i = 0; i < cur_dev->node_info->num_bcm_devs; i++) {
+ cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[i]);
+ cur_vcd = cur_bcm->bcmdev->clk_domain;
- if (!cur_bcm->node_info->num_rsc_devs)
- goto exit_bcm_clist_add;
+ if (!cur_bcm->node_info->num_rsc_devs)
+ goto exit_bcm_clist_add;
- if (!cur_rsc)
- cur_rsc = to_msm_bus_node(cur_bcm->node_info->rsc_devs[0]);
+ if (!cur_rsc)
+ cur_rsc = to_msm_bus_node(cur_bcm->node_info->
+ rsc_devs[0]);
- if (!cur_bcm->dirty) {
- list_add_tail(&cur_bcm->link,
+ if (!cur_bcm->dirty) {
+ list_add_tail(&cur_bcm->link,
&cur_rsc->rscdev->bcm_clist[cur_vcd]);
- cur_bcm->dirty = true;
+ cur_bcm->dirty = true;
+ }
+ cur_bcm->updated = false;
}
- cur_bcm->updated = false;
exit_bcm_clist_add:
return ret;
@@ -462,17 +466,20 @@
{
int ret = 0;
int cur_vcd = 0;
+ int i = 0;
struct msm_bus_node_device_type *cur_bcm = NULL;
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_query_list_add;
- cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[0]);
- cur_vcd = cur_bcm->bcmdev->clk_domain;
+ for (i = 0; i < cur_dev->node_info->num_bcm_devs; i++) {
+ cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[i]);
+ cur_vcd = cur_bcm->bcmdev->clk_domain;
- if (!cur_bcm->query_dirty)
- list_add_tail(&cur_bcm->query_link,
+ if (!cur_bcm->query_dirty)
+ list_add_tail(&cur_bcm->query_link,
&bcm_query_list_inorder[cur_vcd]);
+ }
exit_bcm_query_list_add:
return ret;
@@ -481,20 +488,23 @@
static int bcm_clist_clean(struct msm_bus_node_device_type *cur_dev)
{
int ret = 0;
+ int i = 0;
struct msm_bus_node_device_type *cur_bcm = NULL;
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_clist_clean;
- cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[0]);
+ for (i = 0; i < cur_dev->node_info->num_bcm_devs; i++) {
+ cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[i]);
- if (cur_bcm->node_vec[DUAL_CTX].vec_a == 0 &&
+ if (cur_bcm->node_vec[DUAL_CTX].vec_a == 0 &&
cur_bcm->node_vec[ACTIVE_CTX].vec_a == 0 &&
cur_bcm->node_vec[DUAL_CTX].vec_b == 0 &&
cur_bcm->node_vec[ACTIVE_CTX].vec_b == 0 &&
init_time == false) {
- cur_bcm->dirty = false;
- list_del_init(&cur_bcm->link);
+ cur_bcm->dirty = false;
+ list_del_init(&cur_bcm->link);
+ }
}
exit_bcm_clist_clean:
@@ -504,15 +514,18 @@
static int bcm_query_list_clean(struct msm_bus_node_device_type *cur_dev)
{
int ret = 0;
+ int i = 0;
struct msm_bus_node_device_type *cur_bcm = NULL;
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_clist_add;
- cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[0]);
+ for (i = 0; i < cur_dev->node_info->num_bcm_devs; i++) {
+ cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[i]);
- cur_bcm->query_dirty = false;
- list_del_init(&cur_bcm->query_link);
+ cur_bcm->query_dirty = false;
+ list_del_init(&cur_bcm->query_link);
+ }
exit_bcm_clist_add:
return ret;
@@ -1081,7 +1094,7 @@
static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata,
struct device *bus_dev)
{
- int ret = 0;
+ int ret = 0, i = 0;
struct msm_bus_node_info_type *node_info = NULL;
struct msm_bus_node_info_type *pdata_node_info = NULL;
struct msm_bus_node_device_type *bus_node = NULL;
@@ -1100,7 +1113,17 @@
node_info->name = pdata_node_info->name;
node_info->id = pdata_node_info->id;
- node_info->bcm_req_idx = -1;
+ node_info->bcm_req_idx = devm_kzalloc(bus_dev,
+ sizeof(int) * pdata_node_info->num_bcm_devs,
+ GFP_KERNEL);
+ if (!node_info->bcm_req_idx) {
+ ret = -ENOMEM;
+ goto exit_copy_node_info;
+ }
+
+ for (i = 0; i < pdata_node_info->num_bcm_devs; i++)
+ node_info->bcm_req_idx[i] = -1;
+
node_info->bus_device_id = pdata_node_info->bus_device_id;
node_info->mas_rpm_id = pdata_node_info->mas_rpm_id;
node_info->slv_rpm_id = pdata_node_info->slv_rpm_id;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
index fad7afa..f7f17c3 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
@@ -71,12 +71,16 @@
struct nodevector {
uint64_t vec_a;
uint64_t vec_b;
+ uint64_t query_vec_a;
+ uint64_t query_vec_b;
};
struct msm_bus_rsc_device_type {
struct rpmh_client *mbox;
struct list_head bcm_clist[VCD_MAX_CNT];
int req_state;
+ uint32_t acv[NUM_CTX];
+ uint32_t query_acv[NUM_CTX];
};
struct msm_bus_bcm_device_type {
@@ -157,7 +161,7 @@
struct device **black_connections;
struct device **bcm_devs;
struct device **rsc_devs;
- int bcm_req_idx;
+ int *bcm_req_idx;
unsigned int bus_device_id;
struct device *bus_device;
struct rule_update_path_info rule;
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 11e1b4d..1f28712 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -464,6 +464,8 @@
if (region == NULL) {
pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n",
size);
+ priv->region_start = 0;
+ priv->region_end = 0;
return -ENOMEM;
}
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 62e2384..221ae0c 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -373,13 +373,6 @@
mutex_unlock(&qmi_client_release_lock);
pr_info("Connection established between QMI handle and %d service\n",
data->instance_id);
- /* Register for indication messages about service */
- rc = qmi_register_ind_cb(data->clnt_handle, root_service_service_ind_cb,
- (void *)data);
- if (rc < 0)
- pr_err("Indication callback register failed(instance-id: %d) rc:%d\n",
- data->instance_id, rc);
-
mutex_lock(¬if_add_lock);
mutex_lock(&service_list_lock);
list_for_each_entry(service_notif, &service_list, list) {
@@ -402,6 +395,12 @@
}
mutex_unlock(&service_list_lock);
mutex_unlock(¬if_add_lock);
+ /* Register for indication messages about service */
+ rc = qmi_register_ind_cb(data->clnt_handle,
+ root_service_service_ind_cb, (void *)data);
+ if (rc < 0)
+ pr_err("Indication callback register failed(instance-id: %d) rc:%d\n",
+ data->instance_id, rc);
}
static void root_service_service_exit(struct qmi_client_info *data,
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 119ede3..8aff84c 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -36,6 +36,7 @@
#include <soc/qcom/boot_stats.h>
#define BUILD_ID_LENGTH 32
+#define CHIP_ID_LENGTH 32
#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
#define SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE 128
#define SMEM_IMAGE_VERSION_SIZE 4096
@@ -199,6 +200,20 @@
struct socinfo_v0_13 {
struct socinfo_v0_12 v0_12;
uint32_t nproduct_id;
+ char chip_name[CHIP_ID_LENGTH];
+};
+
+struct socinfo_v0_14 {
+ struct socinfo_v0_13 v0_13;
+ uint32_t num_clusters;
+ uint32_t ncluster_array_offset;
+ uint32_t num_defective_parts;
+ uint32_t ndefective_parts_array_offset;
+};
+
+struct socinfo_v0_15 {
+ struct socinfo_v0_14 v0_14;
+ uint32_t nmodem_supported;
};
static union {
@@ -215,10 +230,12 @@
struct socinfo_v0_11 v0_11;
struct socinfo_v0_12 v0_12;
struct socinfo_v0_13 v0_13;
+ struct socinfo_v0_14 v0_14;
+ struct socinfo_v0_15 v0_15;
} *socinfo;
/* max socinfo format version supported */
-#define MAX_SOCINFO_FORMAT SOCINFO_VERSION(0, 13)
+#define MAX_SOCINFO_FORMAT SOCINFO_VERSION(0, 15)
static struct msm_soc_info cpu_of_id[] = {
@@ -705,6 +722,14 @@
: 0;
}
+static char *socinfo_get_chip_name(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 13) ?
+ socinfo->v0_13.chip_name : "N/A")
+ : "N/A";
+}
+
static uint32_t socinfo_get_nproduct_id(void)
{
return socinfo ?
@@ -713,6 +738,46 @@
: 0;
}
+static uint32_t socinfo_get_num_clusters(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 14) ?
+ socinfo->v0_14.num_clusters : 0)
+ : 0;
+}
+
+static uint32_t socinfo_get_ncluster_array_offset(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 14) ?
+ socinfo->v0_14.ncluster_array_offset : 0)
+ : 0;
+}
+
+static uint32_t socinfo_get_num_defective_parts(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 14) ?
+ socinfo->v0_14.num_defective_parts : 0)
+ : 0;
+}
+
+static uint32_t socinfo_get_ndefective_parts_array_offset(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 14) ?
+ socinfo->v0_14.ndefective_parts_array_offset : 0)
+ : 0;
+}
+
+static uint32_t socinfo_get_nmodem_supported(void)
+{
+ return socinfo ?
+ (socinfo_format >= SOCINFO_VERSION(0, 15) ?
+ socinfo->v0_15.nmodem_supported : 0)
+ : 0;
+}
+
enum pmic_model socinfo_get_pmic_model(void)
{
return socinfo ?
@@ -890,6 +955,15 @@
}
static ssize_t
+msm_get_chip_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ socinfo_get_chip_name());
+}
+
+static ssize_t
msm_get_nproduct_id(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -899,6 +973,51 @@
}
static ssize_t
+msm_get_num_clusters(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n",
+ socinfo_get_num_clusters());
+}
+
+static ssize_t
+msm_get_ncluster_array_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n",
+ socinfo_get_ncluster_array_offset());
+}
+
+static ssize_t
+msm_get_num_defective_parts(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n",
+ socinfo_get_num_defective_parts());
+}
+
+static ssize_t
+msm_get_ndefective_parts_array_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n",
+ socinfo_get_ndefective_parts_array_offset());
+}
+
+static ssize_t
+msm_get_nmodem_supported(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%x\n",
+ socinfo_get_nmodem_supported());
+}
+
+static ssize_t
msm_get_pmic_model(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1146,10 +1265,34 @@
__ATTR(raw_device_number, S_IRUGO,
msm_get_raw_device_number, NULL);
+static struct device_attribute msm_soc_attr_chip_name =
+ __ATTR(chip_name, 0444,
+ msm_get_chip_name, NULL);
+
static struct device_attribute msm_soc_attr_nproduct_id =
__ATTR(nproduct_id, 0444,
msm_get_nproduct_id, NULL);
+static struct device_attribute msm_soc_attr_num_clusters =
+ __ATTR(num_clusters, 0444,
+ msm_get_num_clusters, NULL);
+
+static struct device_attribute msm_soc_attr_ncluster_array_offset =
+ __ATTR(ncluster_array_offset, 0444,
+ msm_get_ncluster_array_offset, NULL);
+
+static struct device_attribute msm_soc_attr_num_defective_parts =
+ __ATTR(num_defective_parts, 0444,
+ msm_get_num_defective_parts, NULL);
+
+static struct device_attribute msm_soc_attr_ndefective_parts_array_offset =
+ __ATTR(ndefective_parts_array_offset, 0444,
+ msm_get_ndefective_parts_array_offset, NULL);
+
+static struct device_attribute msm_soc_attr_nmodem_supported =
+ __ATTR(nmodem_supported, 0444,
+ msm_get_nmodem_supported, NULL);
+
static struct device_attribute msm_soc_attr_pmic_model =
__ATTR(pmic_model, S_IRUGO,
msm_get_pmic_model, NULL);
@@ -1280,9 +1423,23 @@
device_create_file(msm_soc_device, &images);
switch (socinfo_format) {
+ case SOCINFO_VERSION(0, 15):
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_nmodem_supported);
+ case SOCINFO_VERSION(0, 14):
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_num_clusters);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_ncluster_array_offset);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_num_defective_parts);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_ndefective_parts_array_offset);
case SOCINFO_VERSION(0, 13):
device_create_file(msm_soc_device,
&msm_soc_attr_nproduct_id);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_chip_name);
case SOCINFO_VERSION(0, 12):
device_create_file(msm_soc_device,
&msm_soc_attr_chip_family);
@@ -1522,6 +1679,53 @@
socinfo->v0_13.nproduct_id);
break;
+ case SOCINFO_VERSION(0, 14):
+ pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n accessory_chip=%u, hw_plat_subtype=%u, pmic_model=%u, pmic_die_revision=%u foundry_id=%u serial_number=%u num_pmics=%u chip_family=0x%x raw_device_family=0x%x raw_device_number=0x%x nproduct_id=0x%x num_clusters=0x%x ncluster_array_offset=0x%x num_defective_parts=0x%x ndefective_parts_array_offset=0x%x\n",
+ f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+ socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+ socinfo->v0_3.hw_platform,
+ socinfo->v0_4.platform_version,
+ socinfo->v0_5.accessory_chip,
+ socinfo->v0_6.hw_platform_subtype,
+ socinfo->v0_7.pmic_model,
+ socinfo->v0_7.pmic_die_revision,
+ socinfo->v0_9.foundry_id,
+ socinfo->v0_10.serial_number,
+ socinfo->v0_11.num_pmics,
+ socinfo->v0_12.chip_family,
+ socinfo->v0_12.raw_device_family,
+ socinfo->v0_12.raw_device_number,
+ socinfo->v0_13.nproduct_id,
+ socinfo->v0_14.num_clusters,
+ socinfo->v0_14.ncluster_array_offset,
+ socinfo->v0_14.num_defective_parts,
+ socinfo->v0_14.ndefective_parts_array_offset);
+ break;
+
+ case SOCINFO_VERSION(0, 15):
+ pr_info("v%u.%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n accessory_chip=%u, hw_plat_subtype=%u, pmic_model=%u, pmic_die_revision=%u foundry_id=%u serial_number=%u num_pmics=%u chip_family=0x%x raw_device_family=0x%x raw_device_number=0x%x nproduct_id=0x%x num_clusters=0x%x ncluster_array_offset=0x%x num_defective_parts=0x%x ndefective_parts_array_offset=0x%x nmodem_supported=0x%x\n",
+ f_maj, f_min, socinfo->v0_1.id, v_maj, v_min,
+ socinfo->v0_2.raw_id, socinfo->v0_2.raw_version,
+ socinfo->v0_3.hw_platform,
+ socinfo->v0_4.platform_version,
+ socinfo->v0_5.accessory_chip,
+ socinfo->v0_6.hw_platform_subtype,
+ socinfo->v0_7.pmic_model,
+ socinfo->v0_7.pmic_die_revision,
+ socinfo->v0_9.foundry_id,
+ socinfo->v0_10.serial_number,
+ socinfo->v0_11.num_pmics,
+ socinfo->v0_12.chip_family,
+ socinfo->v0_12.raw_device_family,
+ socinfo->v0_12.raw_device_number,
+ socinfo->v0_13.nproduct_id,
+ socinfo->v0_14.num_clusters,
+ socinfo->v0_14.ncluster_array_offset,
+ socinfo->v0_14.num_defective_parts,
+ socinfo->v0_14.ndefective_parts_array_offset,
+ socinfo->v0_15.nmodem_supported);
+ break;
+
default:
pr_err("Unknown format found: v%u.%u\n", f_maj, f_min);
break;
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index e7c2bb2..21f3580 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -481,17 +481,21 @@
mutex_unlock(&subsys_list_lock);
}
-static void for_each_subsys_device(struct subsys_device **list,
+static int for_each_subsys_device(struct subsys_device **list,
unsigned int count, void *data,
- void (*fn)(struct subsys_device *, void *))
+ int (*fn)(struct subsys_device *, void *))
{
+ int ret;
while (count--) {
struct subsys_device *dev = *list++;
if (!dev)
continue;
- fn(dev, data);
+ ret = fn(dev, data);
+ if (ret)
+ return ret;
}
+ return 0;
}
static void notify_each_subsys_device(struct subsys_device **list,
@@ -593,21 +597,31 @@
return 0;
}
-static void subsystem_shutdown(struct subsys_device *dev, void *data)
+static int subsystem_shutdown(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
+ int ret;
pr_info("[%s:%d]: Shutting down %s\n",
current->comm, current->pid, name);
- if (dev->desc->shutdown(dev->desc, true) < 0)
- panic("subsys-restart: [%s:%d]: Failed to shutdown %s!",
- current->comm, current->pid, name);
+ ret = dev->desc->shutdown(dev->desc, true);
+ if (ret < 0) {
+ if (!dev->desc->ignore_ssr_failure) {
+ panic("subsys-restart: [%s:%d]: Failed to shutdown %s!",
+ current->comm, current->pid, name);
+ } else {
+ pr_err("Shutdown failure on %s\n", name);
+ return ret;
+ }
+ }
dev->crash_count++;
subsys_set_state(dev, SUBSYS_OFFLINE);
disable_all_irqs(dev);
+
+ return 0;
}
-static void subsystem_ramdump(struct subsys_device *dev, void *data)
+static int subsystem_ramdump(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
@@ -616,15 +630,17 @@
pr_warn("%s[%s:%d]: Ramdump failed.\n",
name, current->comm, current->pid);
dev->do_ramdump_on_put = false;
+ return 0;
}
-static void subsystem_free_memory(struct subsys_device *dev, void *data)
+static int subsystem_free_memory(struct subsys_device *dev, void *data)
{
if (dev->desc->free_memory)
dev->desc->free_memory(dev->desc);
+ return 0;
}
-static void subsystem_powerup(struct subsys_device *dev, void *data)
+static int subsystem_powerup(struct subsys_device *dev, void *data)
{
const char *name = dev->desc->name;
int ret;
@@ -632,11 +648,17 @@
pr_info("[%s:%d]: Powering up %s\n", current->comm, current->pid, name);
init_completion(&dev->err_ready);
- if (dev->desc->powerup(dev->desc) < 0) {
+ ret = dev->desc->powerup(dev->desc);
+ if (ret < 0) {
notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
NULL);
- panic("[%s:%d]: Powerup error: %s!",
- current->comm, current->pid, name);
+ if (!dev->desc->ignore_ssr_failure) {
+ panic("[%s:%d]: Powerup error: %s!",
+ current->comm, current->pid, name);
+ } else {
+ pr_err("Powerup failure on %s\n", name);
+ return ret;
+ }
}
enable_all_irqs(dev);
@@ -644,11 +666,16 @@
if (ret) {
notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
NULL);
- panic("[%s:%d]: Timed out waiting for error ready: %s!",
- current->comm, current->pid, name);
+ if (!dev->desc->ignore_ssr_failure)
+ panic("[%s:%d]: Timed out waiting for error ready: %s!",
+ current->comm, current->pid, name);
+ else
+ return ret;
}
subsys_set_state(dev, SUBSYS_ONLINE);
subsys_set_crash_status(dev, CRASH_STATUS_NO_CRASH);
+
+ return 0;
}
static int __find_subsys(struct device *dev, void *data)
@@ -910,6 +937,7 @@
struct subsys_tracking *track;
unsigned int count;
unsigned long flags;
+ int ret;
/*
* It's OK to not take the registration lock at this point.
@@ -957,7 +985,9 @@
pr_debug("[%s:%d]: Starting restart sequence for %s\n",
current->comm, current->pid, desc->name);
notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
- for_each_subsys_device(list, count, NULL, subsystem_shutdown);
+ ret = for_each_subsys_device(list, count, NULL, subsystem_shutdown);
+ if (ret)
+ goto err;
notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
@@ -973,12 +1003,19 @@
for_each_subsys_device(list, count, NULL, subsystem_free_memory);
notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
- for_each_subsys_device(list, count, NULL, subsystem_powerup);
+ ret = for_each_subsys_device(list, count, NULL, subsystem_powerup);
+ if (ret)
+ goto err;
notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
pr_info("[%s:%d]: Restart sequence for %s completed.\n",
current->comm, current->pid, desc->name);
+err:
+ /* Reset subsys count */
+ if (ret)
+ dev->count = 0;
+
mutex_unlock(&soc_order_reg_lock);
mutex_unlock(&track->lock);
@@ -1470,6 +1507,9 @@
desc->generic_irq = ret;
}
+ desc->ignore_ssr_failure = of_property_read_bool(pdev->dev.of_node,
+ "qcom,ignore-ssr-failure");
+
order = ssr_parse_restart_orders(desc);
if (IS_ERR(order)) {
pr_err("Could not initialize SSR restart order, err = %ld\n",
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 7a784aa..8bf5659 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -136,6 +136,8 @@
return 0;
__raw_writel(1, wdog_dd->base + WDT0_RST);
if (wdog_dd->wakeup_irq_enable) {
+ /* Make sure register write is complete before proceeding */
+ mb();
wdog_dd->last_pet = sched_clock();
return 0;
}
@@ -151,8 +153,15 @@
{
struct msm_watchdog_data *wdog_dd =
(struct msm_watchdog_data *)dev_get_drvdata(dev);
- if (!enable || wdog_dd->wakeup_irq_enable)
+ if (!enable)
return 0;
+ if (wdog_dd->wakeup_irq_enable) {
+ __raw_writel(1, wdog_dd->base + WDT0_RST);
+ /* Make sure register write is complete before proceeding */
+ mb();
+ wdog_dd->last_pet = sched_clock();
+ return 0;
+ }
__raw_writel(1, wdog_dd->base + WDT0_EN);
__raw_writel(1, wdog_dd->base + WDT0_RST);
/* Make sure watchdog is reset before setting enable */
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index a5bfeab..9cc85ee 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -584,6 +584,11 @@
id = ffs(status) - 1;
status &= ~BIT(id);
apid = id + i * 32;
+ if (apid < pa->min_apid || apid > pa->max_apid) {
+ WARN_ONCE(true, "spurious spmi irq received for apid=%d\n",
+ apid);
+ continue;
+ }
enable = readl_relaxed(pa->intr +
pa->ver_ops->acc_enable(apid));
if (enable & SPMI_PIC_ACC_ENABLE_BIT)
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
index 8d6bca6..591f274 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
@@ -97,8 +97,9 @@
switch (variable) {
case HW_VAR_BSSID:
- rtl92e_writel(dev, BSSIDR, ((u32 *)(val))[0]);
- rtl92e_writew(dev, BSSIDR+2, ((u16 *)(val+2))[0]);
+ /* BSSIDR 2 byte alignment */
+ rtl92e_writew(dev, BSSIDR, *(u16 *)val);
+ rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(val + 2));
break;
case HW_VAR_MEDIA_STATUS:
@@ -626,7 +627,7 @@
struct r8192_priv *priv = rtllib_priv(dev);
RT_TRACE(COMP_INIT, "===========>%s()\n", __func__);
- curCR = rtl92e_readl(dev, EPROM_CMD);
+ curCR = rtl92e_readw(dev, EPROM_CMD);
RT_TRACE(COMP_INIT, "read from Reg Cmd9346CR(%x):%x\n", EPROM_CMD,
curCR);
priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 :
@@ -963,8 +964,8 @@
rtl92e_config_rate(dev, &rate_config);
priv->dot11CurrentPreambleMode = PREAMBLE_AUTO;
priv->basic_rate = rate_config &= 0x15f;
- rtl92e_writel(dev, BSSIDR, ((u32 *)net->bssid)[0]);
- rtl92e_writew(dev, BSSIDR+4, ((u16 *)net->bssid)[2]);
+ rtl92e_writew(dev, BSSIDR, *(u16 *)net->bssid);
+ rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(net->bssid + 2));
if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
rtl92e_writew(dev, ATIMWND, 2);
@@ -1184,8 +1185,7 @@
struct cb_desc *cb_desc, struct sk_buff *skb)
{
struct r8192_priv *priv = rtllib_priv(dev);
- dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
+ dma_addr_t mapping;
struct tx_fwinfo_8190pci *pTxFwInfo;
pTxFwInfo = (struct tx_fwinfo_8190pci *)skb->data;
@@ -1196,8 +1196,6 @@
pTxFwInfo->Short = _rtl92e_query_is_short(pTxFwInfo->TxHT,
pTxFwInfo->TxRate, cb_desc);
- if (pci_dma_mapping_error(priv->pdev, mapping))
- netdev_err(dev, "%s(): DMA Mapping error\n", __func__);
if (cb_desc->bAMPDUEnable) {
pTxFwInfo->AllowAggregation = 1;
pTxFwInfo->RxMF = cb_desc->ampdu_factor;
@@ -1232,6 +1230,14 @@
}
memset((u8 *)pdesc, 0, 12);
+
+ mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pdev, mapping)) {
+ netdev_err(dev, "%s(): DMA Mapping error\n", __func__);
+ return;
+ }
+
pdesc->LINIP = 0;
pdesc->CmdInit = 1;
pdesc->Offset = sizeof(struct tx_fwinfo_8190pci) + 8;
diff --git a/drivers/staging/rtl8192e/rtl819x_TSProc.c b/drivers/staging/rtl8192e/rtl819x_TSProc.c
index a966a8e..4615a6f 100644
--- a/drivers/staging/rtl8192e/rtl819x_TSProc.c
+++ b/drivers/staging/rtl8192e/rtl819x_TSProc.c
@@ -306,11 +306,6 @@
pTsCommonInfo->TClasNum = TCLAS_Num;
}
-static bool IsACValid(unsigned int tid)
-{
- return tid < 7;
-}
-
bool GetTs(struct rtllib_device *ieee, struct ts_common_info **ppTS,
u8 *Addr, u8 TID, enum tr_select TxRxSelect, bool bAddNewTs)
{
@@ -328,12 +323,6 @@
if (ieee->current_network.qos_data.supported == 0) {
UP = 0;
} else {
- if (!IsACValid(TID)) {
- netdev_warn(ieee->dev, "%s(): TID(%d) is not valid\n",
- __func__, TID);
- return false;
- }
-
switch (TID) {
case 0:
case 3:
@@ -351,6 +340,10 @@
case 7:
UP = 7;
break;
+ default:
+ netdev_warn(ieee->dev, "%s(): TID(%d) is not valid\n",
+ __func__, TID);
+ return false;
}
}
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 7da9211..355d013 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -620,6 +620,7 @@
if (cpufreq_device->cpufreq_state == state)
return 0;
+ cpufreq_device->cpufreq_state = state;
/* If state is the last, isolate the CPU */
if (state == cpufreq_device->max_level)
return sched_isolate_cpu(cpu);
@@ -627,7 +628,6 @@
sched_unisolate_cpu(cpu);
clip_freq = cpufreq_device->freq_table[state];
- cpufreq_device->cpufreq_state = state;
cpufreq_device->clipped_freq = clip_freq;
/* Check if the device has a platform mitigation function that
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 342160e..04320d8 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -1973,8 +1973,6 @@
chip->sensor[sen_idx].thermal_node = true;
snprintf(name, sizeof(name), "%s",
chip->adc->adc_channels[sen_idx].name);
- chip->sensor[sen_idx].meas_interval =
- QPNP_ADC_TM_MEAS_INTERVAL;
chip->sensor[sen_idx].low_thr =
QPNP_ADC_TM_M0_LOW_THR;
chip->sensor[sen_idx].high_thr =
@@ -2027,7 +2025,7 @@
rc = devm_request_irq(&pdev->dev, chip->adc->adc_irq_eoc,
qpnp_adc_tm_rc_thr_isr,
- IRQF_TRIGGER_RISING, "qpnp_adc_tm_interrupt", chip);
+ IRQF_TRIGGER_HIGH, "qpnp_adc_tm_interrupt", chip);
if (rc)
dev_err(&pdev->dev, "failed to request adc irq\n");
else
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 6deff2e..2e12c3f 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -1020,6 +1020,7 @@
disable_irq(uport->irq);
free_irq(uport->irq, msm_port);
if (uart_console(uport)) {
+ console_stop(uport->cons);
se_geni_resources_off(&msm_port->serial_rsc);
} else {
if (msm_port->wakeup_irq > 0) {
@@ -1845,7 +1846,6 @@
if (uart_console(uport)) {
uart_suspend_port((struct uart_driver *)uport->private_data,
uport);
- se_geni_resources_off(&port->serial_rsc);
} else {
if (!pm_runtime_status_suspended(dev)) {
dev_info(dev, "%s: Is still active\n", __func__);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c5ff13f..a876d47 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -311,6 +311,12 @@
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
+ if (le16_to_cpu(dr->wLength) != 2) {
+ dev_dbg(&acm->control->dev,
+ "%s - malformed serial state\n", __func__);
+ break;
+ }
+
newctrl = get_unaligned_le16(data);
if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
@@ -347,11 +353,10 @@
default:
dev_dbg(&acm->control->dev,
- "%s - unknown notification %d received: index %d "
- "len %d data0 %d data1 %d\n",
+ "%s - unknown notification %d received: index %d len %d\n",
__func__,
- dr->bNotificationType, dr->wIndex,
- dr->wLength, data[0], data[1]);
+ dr->bNotificationType, dr->wIndex, dr->wLength);
+
break;
}
exit:
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 4016dae..840930b0 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -481,11 +481,11 @@
if (userurb) { /* Async */
if (when == SUBMIT)
- dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+ dev_info(&udev->dev, "userurb %pK, ep%d %s-%s, "
"length %u\n",
userurb, ep, t, d, length);
else
- dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+ dev_info(&udev->dev, "userurb %pK, ep%d %s-%s, "
"actual_length %u status %d\n",
userurb, ep, t, d, length,
timeout_or_status);
@@ -1905,7 +1905,7 @@
if (as) {
int retval;
- snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ snoop(&ps->dev->dev, "reap %pK\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
return retval;
@@ -1922,7 +1922,7 @@
as = async_getcompleted(ps);
if (as) {
- snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ snoop(&ps->dev->dev, "reap %pK\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
} else {
@@ -2053,7 +2053,7 @@
if (as) {
int retval;
- snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ snoop(&ps->dev->dev, "reap %pK\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
return retval;
@@ -2070,7 +2070,7 @@
as = async_getcompleted(ps);
if (as) {
- snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ snoop(&ps->dev->dev, "reap %pK\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
} else {
@@ -2499,7 +2499,7 @@
#endif
case USBDEVFS_DISCARDURB:
- snoop(&dev->dev, "%s: DISCARDURB %p\n", __func__, p);
+ snoop(&dev->dev, "%s: DISCARDURB %pK\n", __func__, p);
ret = proc_unlinkurb(ps, p);
break;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index ff45ebf..32f99da 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1723,7 +1723,7 @@
if (retval == 0)
retval = -EINPROGRESS;
else if (retval != -EIDRM && retval != -EBUSY)
- dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
+ dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
urb, retval);
usb_put_dev(udev);
}
@@ -1890,7 +1890,7 @@
/* kick hcd */
unlink1(hcd, urb, -ESHUTDOWN);
dev_dbg (hcd->self.controller,
- "shutdown urb %p ep%d%s%s\n",
+ "shutdown urb %pK ep%d%s%s\n",
urb, usb_endpoint_num(&ep->desc),
is_in ? "in" : "out",
({ char *s;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 7388f73..fcbaa61 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -365,7 +365,8 @@
}
/* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data)
+static int get_hub_descriptor(struct usb_device *hdev,
+ struct usb_hub_descriptor *desc)
{
int i, ret, size;
unsigned dtype;
@@ -381,10 +382,18 @@
for (i = 0; i < 3; i++) {
ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
- dtype << 8, 0, data, size,
+ dtype << 8, 0, desc, size,
USB_CTRL_GET_TIMEOUT);
- if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
+ if (hub_is_superspeed(hdev)) {
+ if (ret == size)
+ return ret;
+ } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) {
+ /* Make sure we have the DeviceRemovable field. */
+ size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1;
+ if (ret < size)
+ return -EMSGSIZE;
return ret;
+ }
}
return -EINVAL;
}
@@ -1322,7 +1331,7 @@
}
mutex_init(&hub->status_mutex);
- hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+ hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
ret = -ENOMEM;
goto fail;
@@ -1330,7 +1339,7 @@
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
- * but the hub can/will return fewer bytes here.
+ * but a (non-SS) hub can/will return fewer bytes here.
*/
ret = get_hub_descriptor(hdev, hub->descriptor);
if (ret < 0) {
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index a903969..5133ab9 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -333,7 +333,7 @@
if (!urb || !urb->complete)
return -EINVAL;
if (urb->hcpriv) {
- WARN_ONCE(1, "URB %p submitted while active\n", urb);
+ WARN_ONCE(1, "URB %pK submitted while active\n", urb);
return -EBUSY;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 62574bf..026ff6c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -990,11 +990,16 @@
}
/* always enable Continue on Short Packet */
- trb->ctrl |= DWC3_TRB_CTRL_CSP;
+ if (usb_endpoint_dir_out(dep->endpoint.desc)) {
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
+
+ if (req->request.short_not_ok)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ }
if ((!req->request.no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
- trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
@@ -3419,13 +3424,13 @@
trace_dwc3_event(event->raw);
/* skip event processing in absence of vbus */
if (!dwc->vbus_active) {
- dev_err(dwc->dev, "SKIP EVT:%x", event->raw);
+ dbg_event(0xFF, "SKIP_EVT", event->raw);
return;
}
/* If run/stop is cleared don't process any more events */
if (!dwc->pullups_connected) {
- dev_err(dwc->dev, "SKIP_EVT_PULLUP:%x", event->raw);
+ dbg_event(0xFF, "SKIP_EVT_PULLUP", event->raw);
return;
}
@@ -3549,6 +3554,15 @@
evt = dwc->ev_buf;
+ /*
+ * With PCIe legacy interrupt, test shows that top-half irq handler can
+ * be called again after HW interrupt deassertion. Check if bottom-half
+ * irq event handler completes before caching new event to prevent
+ * losing events.
+ */
+ if (evt->flags & DWC3_EVENT_PENDING)
+ return IRQ_HANDLED;
+
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 86612ac..f6c7a27 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -231,7 +231,8 @@
/* Start up the I/O watchdog timer, if it's not running */
if (!timer_pending(&ohci->io_watchdog) &&
- list_empty(&ohci->eds_in_use)) {
+ list_empty(&ohci->eds_in_use) &&
+ !(ohci->flags & OHCI_QUIRK_QEMU)) {
ohci->prev_frame_no = ohci_frame_no(ohci);
mod_timer(&ohci->io_watchdog,
jiffies + IO_WATCHDOG_DELAY);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index bb15096..a84aebe 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -164,6 +164,15 @@
return 0;
}
+static int ohci_quirk_qemu(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci->flags |= OHCI_QUIRK_QEMU;
+ ohci_dbg(ohci, "enabled qemu quirk\n");
+ return 0;
+}
+
/* List of quirks for OHCI */
static const struct pci_device_id ohci_pci_quirks[] = {
{
@@ -214,6 +223,13 @@
PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399),
.driver_data = (unsigned long)ohci_quirk_amd700,
},
+ {
+ .vendor = PCI_VENDOR_ID_APPLE,
+ .device = 0x003f,
+ .subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
+ .subdevice = PCI_SUBDEVICE_ID_QEMU,
+ .driver_data = (unsigned long)ohci_quirk_qemu,
+ },
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 37f1725..a51b189 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -418,6 +418,7 @@
#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
#define OHCI_QUIRK_GLOBAL_SUSPEND 0x800 /* must suspend ports */
+#define OHCI_QUIRK_QEMU 0x1000 /* relax timing expectations */
// there are also chip quirks/bugs in init logic
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 40504c8..d680eb3 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -56,7 +56,7 @@
}
if (max_packet) {
- seg->bounce_buf = kzalloc(max_packet, flags | GFP_DMA);
+ seg->bounce_buf = kzalloc(max_packet, flags);
if (!seg->bounce_buf) {
dma_pool_free(xhci->segment_pool, seg->trbs, dma);
kfree(seg);
@@ -1726,7 +1726,7 @@
xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
- void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
+ void *buf = dma_zalloc_coherent(dev, xhci->page_size, &dma,
flags);
if (!buf)
goto fail_sp5;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 93f566c..e7d6752 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -52,6 +52,7 @@
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
+#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
static const char hcd_name[] = "xhci_hcd";
@@ -166,7 +167,8 @@
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
@@ -175,7 +177,8 @@
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
xhci->quirks |= XHCI_MISSING_CAS;
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index a0bc61f..6cb5ab3 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -210,7 +210,7 @@
irq = platform_get_irq(pdev, 0);
if (irq < 0)
- return -ENODEV;
+ return irq;
/*
* sysdev must point to a device that is known to the system firmware
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 6ddd08a..efecb87 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -194,7 +194,7 @@
dev->in_ep = in_ep;
- if (udev->descriptor.idVendor != ALEA_VENDOR_ID)
+ if (le16_to_cpu(udev->descriptor.idVendor) != ALEA_VENDOR_ID)
dev->reads_started = 1;
dev->size = size;
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 37c63cb..0ef29d2 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -554,7 +554,7 @@
info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
- info.speed = le16_to_cpu(dev->udev->speed);
+ info.speed = dev->udev->speed;
info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
info.report_size = dev->report_size;
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index c8fbe7b..c2e2b2e 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -317,9 +317,16 @@
int subminor;
int retval = 0;
struct usb_interface *interface;
- struct tower_reset_reply reset_reply;
+ struct tower_reset_reply *reset_reply;
int result;
+ reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL);
+
+ if (!reset_reply) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
nonseekable_open(inode, file);
subminor = iminor(inode);
@@ -364,8 +371,8 @@
USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
0,
0,
- &reset_reply,
- sizeof(reset_reply),
+ reset_reply,
+ sizeof(*reset_reply),
1000);
if (result < 0) {
dev_err(&dev->udev->dev,
@@ -406,6 +413,7 @@
mutex_unlock(&dev->lock);
exit:
+ kfree(reset_reply);
return retval;
}
@@ -808,7 +816,7 @@
struct lego_usb_tower *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor* endpoint;
- struct tower_get_version_reply get_version_reply;
+ struct tower_get_version_reply *get_version_reply = NULL;
int i;
int retval = -ENOMEM;
int result;
@@ -886,6 +894,13 @@
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
+ get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL);
+
+ if (!get_version_reply) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
@@ -893,18 +908,19 @@
USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
0,
0,
- &get_version_reply,
- sizeof(get_version_reply),
+ get_version_reply,
+ sizeof(*get_version_reply),
1000);
if (result < 0) {
dev_err(idev, "LEGO USB Tower get version control request failed\n");
retval = result;
goto error;
}
- dev_info(&interface->dev, "LEGO USB Tower firmware version is %d.%d "
- "build %d\n", get_version_reply.major,
- get_version_reply.minor,
- le16_to_cpu(get_version_reply.build_no));
+ dev_info(&interface->dev,
+ "LEGO USB Tower firmware version is %d.%d build %d\n",
+ get_version_reply->major,
+ get_version_reply->minor,
+ le16_to_cpu(get_version_reply->build_no));
/* we can register the device now, as it is ready */
usb_set_intfdata (interface, dev);
@@ -925,9 +941,11 @@
USB_MAJOR, dev->minor);
exit:
+ kfree(get_version_reply);
return retval;
error:
+ kfree(get_version_reply);
tower_delete(dev);
return retval;
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 8064514..99beda9 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2780,10 +2780,11 @@
int ret;
struct usb_hcd *hcd = musb->hcd;
- MUSB_HST_MODE(musb);
- musb->xceiv->otg->default_a = 1;
- musb->xceiv->otg->state = OTG_STATE_A_IDLE;
-
+ if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+ MUSB_HST_MODE(musb);
+ musb->xceiv->otg->default_a = 1;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ }
otg_set_host(musb->xceiv->otg, &hcd->self);
hcd->self.otg_port = 1;
musb->xceiv->otg->host = &hcd->self;
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index e6959cc..4047426 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -220,6 +220,7 @@
u32 dma_remaining;
int src_burst, dst_burst;
u16 csr;
+ u32 psize;
int ch;
s8 dmareq;
s8 sync_dev;
@@ -391,15 +392,19 @@
if (chdat->tx) {
/* Send transfer_packet_sz packets at a time */
- musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
- chdat->transfer_packet_sz);
+ psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET);
+ psize &= ~0x7ff;
+ psize |= chdat->transfer_packet_sz;
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize);
musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
} else {
/* Receive transfer_packet_sz packets at a time */
- musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
- chdat->transfer_packet_sz << 16);
+ psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET);
+ psize &= ~(0x7ff << 16);
+ psize |= (chdat->transfer_packet_sz << 16);
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize);
musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 1dc75db..1939496 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -809,10 +809,10 @@
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
{ USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) },
{ USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
@@ -1508,9 +1508,9 @@
(new_serial.flags & ASYNC_FLAGS));
priv->custom_divisor = new_serial.custom_divisor;
+check_and_exit:
write_latency_timer(port);
-check_and_exit:
if ((old_priv.flags & ASYNC_SPD_MASK) !=
(priv->flags & ASYNC_SPD_MASK)) {
if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 71fb9e5..4fcf1ce 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -882,6 +882,8 @@
/* Olimex */
#define OLIMEX_VID 0x15BA
#define OLIMEX_ARM_USB_OCD_PID 0x0003
+#define OLIMEX_ARM_USB_TINY_PID 0x0004
+#define OLIMEX_ARM_USB_TINY_H_PID 0x002a
#define OLIMEX_ARM_USB_OCD_H_PID 0x002b
/*
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index f1a8fdc..e98532f 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2349,8 +2349,11 @@
if (!baud) {
/* pick a default, any default... */
baud = 9600;
- } else
+ } else {
+ /* Avoid a zero divisor. */
+ baud = min(baud, 461550);
tty_encode_baud_rate(tty, baud, baud);
+ }
edge_port->baud_rate = baud;
config->wBaudRate = (__u16)((461550L + baud/2) / baud);
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index edbc81f..70f346f 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -189,7 +189,7 @@
return -ENOMEM;
divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
- put_unaligned_le32(cpu_to_le32(divisor), buf);
+ put_unaligned_le32(divisor, buf);
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_BAUD_RATE_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index af67a0d..3bf61ac 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -281,6 +281,7 @@
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
+#define TELIT_PRODUCT_ME910 0x1100
#define TELIT_PRODUCT_LE920 0x1200
#define TELIT_PRODUCT_LE910 0x1201
#define TELIT_PRODUCT_LE910_USBCFG4 0x1206
@@ -640,6 +641,11 @@
.reserved = BIT(5) | BIT(6),
};
+static const struct option_blacklist_info telit_me910_blacklist = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(1) | BIT(3),
+};
+
static const struct option_blacklist_info telit_le910_blacklist = {
.sendsetup = BIT(0),
.reserved = BIT(1) | BIT(2),
@@ -1235,6 +1241,8 @@
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
+ .driver_info = (kernel_ulong_t)&telit_me910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 38b3f0d..fd509ed6c 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -162,6 +162,8 @@
{DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */
{DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */
+ {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */
+ {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 02bdaa9..4340b49 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -446,6 +446,10 @@
#define SD_BLOCK_LEN 9
struct ene_ub6250_info {
+
+ /* I/O bounce buffer */
+ u8 *bbuf;
+
/* for 6250 code */
struct SD_STATUS SD_Status;
struct MS_STATUS MS_Status;
@@ -493,8 +497,11 @@
static void ene_ub6250_info_destructor(void *extra)
{
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) extra;
+
if (!extra)
return;
+ kfree(info->bbuf);
}
static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg)
@@ -860,8 +867,9 @@
u8 PageNum, u32 *PageBuf, struct ms_lib_type_extdat *ExtraDat)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
int result;
- u8 ExtBuf[4];
u32 bn = PhyBlockAddr * 0x20 + PageNum;
result = ene_load_bincode(us, MS_RW_PATTERN);
@@ -901,7 +909,7 @@
bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16);
bcb->CDB[6] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -910,9 +918,9 @@
ExtraDat->status0 = 0x10; /* Not yet,fireware support */
ExtraDat->status1 = 0x00; /* Not yet,fireware support */
- ExtraDat->ovrflg = ExtBuf[0];
- ExtraDat->mngflg = ExtBuf[1];
- ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]);
+ ExtraDat->ovrflg = bbuf[0];
+ ExtraDat->mngflg = bbuf[1];
+ ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]);
return USB_STOR_TRANSPORT_GOOD;
}
@@ -1332,8 +1340,9 @@
u8 PageNum, struct ms_lib_type_extdat *ExtraDat)
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
+ struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
int result;
- u8 ExtBuf[4];
memset(bcb, 0, sizeof(struct bulk_cb_wrap));
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
@@ -1347,7 +1356,7 @@
bcb->CDB[2] = (unsigned char)(PhyBlock>>16);
bcb->CDB[6] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -1355,9 +1364,9 @@
ExtraDat->intr = 0x80; /* Not yet, waiting for fireware support */
ExtraDat->status0 = 0x10; /* Not yet, waiting for fireware support */
ExtraDat->status1 = 0x00; /* Not yet, waiting for fireware support */
- ExtraDat->ovrflg = ExtBuf[0];
- ExtraDat->mngflg = ExtBuf[1];
- ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]);
+ ExtraDat->ovrflg = bbuf[0];
+ ExtraDat->mngflg = bbuf[1];
+ ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]);
return USB_STOR_TRANSPORT_GOOD;
}
@@ -1558,9 +1567,9 @@
u16 PhyBlock, newblk, i;
u16 LogStart, LogEnde;
struct ms_lib_type_extdat extdat;
- u8 buf[0x200];
u32 count = 0, index = 0;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
for (PhyBlock = 0; PhyBlock < info->MS_Lib.NumberOfPhyBlock;) {
ms_lib_phy_to_log_range(PhyBlock, &LogStart, &LogEnde);
@@ -1574,14 +1583,16 @@
}
if (count == PhyBlock) {
- ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, &buf);
+ ms_lib_read_extrablock(us, PhyBlock, 0, 0x80,
+ bbuf);
count += 0x80;
}
index = (PhyBlock % 0x80) * 4;
- extdat.ovrflg = buf[index];
- extdat.mngflg = buf[index+1];
- extdat.logadr = memstick_logaddr(buf[index+2], buf[index+3]);
+ extdat.ovrflg = bbuf[index];
+ extdat.mngflg = bbuf[index+1];
+ extdat.logadr = memstick_logaddr(bbuf[index+2],
+ bbuf[index+3]);
if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) {
ms_lib_setacquired_errorblock(us, PhyBlock);
@@ -2064,9 +2075,9 @@
{
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
int result;
- u8 buf[0x200];
u16 MSP_BlockSize, MSP_UserAreaBlocks;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
printk(KERN_INFO "transport --- ENE_MSInit\n");
@@ -2085,13 +2096,13 @@
bcb->CDB[0] = 0xF1;
bcb->CDB[1] = 0x01;
- result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD) {
printk(KERN_ERR "Execution MS Init Code Fail !!\n");
return USB_STOR_TRANSPORT_ERROR;
}
/* the same part to test ENE */
- info->MS_Status = *(struct MS_STATUS *)&buf[0];
+ info->MS_Status = *(struct MS_STATUS *) bbuf;
if (info->MS_Status.Insert && info->MS_Status.Ready) {
printk(KERN_INFO "Insert = %x\n", info->MS_Status.Insert);
@@ -2100,15 +2111,15 @@
printk(KERN_INFO "IsMSPHG = %x\n", info->MS_Status.IsMSPHG);
printk(KERN_INFO "WtP= %x\n", info->MS_Status.WtP);
if (info->MS_Status.IsMSPro) {
- MSP_BlockSize = (buf[6] << 8) | buf[7];
- MSP_UserAreaBlocks = (buf[10] << 8) | buf[11];
+ MSP_BlockSize = (bbuf[6] << 8) | bbuf[7];
+ MSP_UserAreaBlocks = (bbuf[10] << 8) | bbuf[11];
info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks;
} else {
ms_card_init(us); /* Card is MS (to ms.c)*/
}
usb_stor_dbg(us, "MS Init Code OK !!\n");
} else {
- usb_stor_dbg(us, "MS Card Not Ready --- %x\n", buf[0]);
+ usb_stor_dbg(us, "MS Card Not Ready --- %x\n", bbuf[0]);
return USB_STOR_TRANSPORT_ERROR;
}
@@ -2118,9 +2129,9 @@
static int ene_sd_init(struct us_data *us)
{
int result;
- u8 buf[0x200];
struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
+ u8 *bbuf = info->bbuf;
usb_stor_dbg(us, "transport --- ENE_SDInit\n");
/* SD Init Part-1 */
@@ -2154,17 +2165,17 @@
bcb->Flags = US_BULK_FLAG_IN;
bcb->CDB[0] = 0xF1;
- result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0);
+ result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0);
if (result != USB_STOR_XFER_GOOD) {
usb_stor_dbg(us, "Execution SD Init Code Fail !!\n");
return USB_STOR_TRANSPORT_ERROR;
}
- info->SD_Status = *(struct SD_STATUS *)&buf[0];
+ info->SD_Status = *(struct SD_STATUS *) bbuf;
if (info->SD_Status.Insert && info->SD_Status.Ready) {
struct SD_STATUS *s = &info->SD_Status;
- ene_get_card_status(us, (unsigned char *)&buf);
+ ene_get_card_status(us, bbuf);
usb_stor_dbg(us, "Insert = %x\n", s->Insert);
usb_stor_dbg(us, "Ready = %x\n", s->Ready);
usb_stor_dbg(us, "IsMMC = %x\n", s->IsMMC);
@@ -2172,7 +2183,7 @@
usb_stor_dbg(us, "HiSpeed = %x\n", s->HiSpeed);
usb_stor_dbg(us, "WtP = %x\n", s->WtP);
} else {
- usb_stor_dbg(us, "SD Card Not Ready --- %x\n", buf[0]);
+ usb_stor_dbg(us, "SD Card Not Ready --- %x\n", bbuf[0]);
return USB_STOR_TRANSPORT_ERROR;
}
return USB_STOR_TRANSPORT_GOOD;
@@ -2182,13 +2193,15 @@
static int ene_init(struct us_data *us)
{
int result;
- u8 misc_reg03 = 0;
+ u8 misc_reg03;
struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra);
+ u8 *bbuf = info->bbuf;
- result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ result = ene_get_card_type(us, REG_CARD_STATUS, bbuf);
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
+ misc_reg03 = bbuf[0];
if (misc_reg03 & 0x01) {
if (!info->SD_Status.Ready) {
result = ene_sd_init(us);
@@ -2305,8 +2318,9 @@
const struct usb_device_id *id)
{
int result;
- u8 misc_reg03 = 0;
+ u8 misc_reg03;
struct us_data *us;
+ struct ene_ub6250_info *info;
result = usb_stor_probe1(&us, intf, id,
(id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list,
@@ -2315,11 +2329,16 @@
return result;
/* FIXME: where should the code alloc extra buf ? */
- if (!us->extra) {
- us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
- if (!us->extra)
- return -ENOMEM;
- us->extra_destructor = ene_ub6250_info_destructor;
+ us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
+ if (!us->extra)
+ return -ENOMEM;
+ us->extra_destructor = ene_ub6250_info_destructor;
+
+ info = (struct ene_ub6250_info *)(us->extra);
+ info->bbuf = kmalloc(512, GFP_KERNEL);
+ if (!info->bbuf) {
+ kfree(us->extra);
+ return -ENOMEM;
}
us->transport_name = "ene_ub6250";
@@ -2331,12 +2350,13 @@
return result;
/* probe card type */
- result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03);
+ result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf);
if (result != USB_STOR_XFER_GOOD) {
usb_stor_disconnect(intf);
return USB_STOR_TRANSPORT_ERROR;
}
+ misc_reg03 = info->bbuf[0];
if (!(misc_reg03 & 0x01)) {
pr_info("ums_eneub6250: This driver only supports SD/MS cards. "
"It does not support SM cards.\n");
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
index 6345e85..a50cf45 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -341,6 +341,7 @@
static
int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
+ struct usb_device *udev = interface_to_usbdev(iface);
struct i1480_usb *i1480_usb;
struct i1480 *i1480;
struct device *dev = &iface->dev;
@@ -352,8 +353,8 @@
iface->cur_altsetting->desc.bInterfaceNumber);
goto error;
}
- if (iface->num_altsetting > 1
- && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
+ if (iface->num_altsetting > 1 &&
+ le16_to_cpu(udev->descriptor.idProduct) == 0xbabe) {
/* Need altsetting #1 [HW QUIRK] or EP1 won't work */
result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
if (result < 0)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1626892..1cf907e 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -533,6 +533,10 @@
return "4:3";
case HDMI_PICTURE_ASPECT_16_9:
return "16:9";
+ case HDMI_PICTURE_ASPECT_64_27:
+ return "64:27";
+ case HDMI_PICTURE_ASPECT_256_135:
+ return "256:135";
case HDMI_PICTURE_ASPECT_RESERVED:
return "Reserved";
}
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
index 99ebf6e..5615f40 100644
--- a/drivers/watchdog/pcwd_usb.c
+++ b/drivers/watchdog/pcwd_usb.c
@@ -630,6 +630,9 @@
return -ENODEV;
}
+ if (iface_desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
/* check out the endpoint: it has to be Interrupt & IN */
endpoint = &iface_desc->endpoint[0].desc;
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c
index 80bb956..d1bbdc9 100644
--- a/fs/crypto/fname.c
+++ b/fs/crypto/fname.c
@@ -300,7 +300,7 @@
} else {
memset(buf, 0, 8);
}
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
oname->len = 1 + digest_encode(buf, 24, oname->name + 1);
return 0;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index c4a389a..423a21c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1255,9 +1255,9 @@
if (unlikely(!name)) {
if (fname->usr_fname->name[0] == '_') {
int ret;
- if (de->name_len < 16)
+ if (de->name_len <= 32)
return 0;
- ret = memcmp(de->name + de->name_len - 16,
+ ret = memcmp(de->name + ((de->name_len - 17) & ~15),
fname->crypto_buf.name + 8, 16);
return (ret == 0) ? 1 : 0;
}
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index ebdc90f..11f3717 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -130,19 +130,29 @@
continue;
}
- /* encrypted case */
+ if (de->hash_code != namehash)
+ goto not_match;
+
de_name.name = d->filename[bit_pos];
de_name.len = le16_to_cpu(de->name_len);
- /* show encrypted name */
- if (fname->hash) {
- if (de->hash_code == fname->hash)
- goto found;
- } else if (de_name.len == name->len &&
- de->hash_code == namehash &&
- !memcmp(de_name.name, name->name, name->len))
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+ if (unlikely(!name->name)) {
+ if (fname->usr_fname->name[0] == '_') {
+ if (de_name.len > 32 &&
+ !memcmp(de_name.name + ((de_name.len - 17) & ~15),
+ fname->crypto_buf.name + 8, 16))
+ goto found;
+ goto not_match;
+ }
+ name->name = fname->crypto_buf.name;
+ name->len = fname->crypto_buf.len;
+ }
+#endif
+ if (de_name.len == name->len &&
+ !memcmp(de_name.name, name->name, name->len))
goto found;
-
+not_match:
if (max_slots && max_len > *max_slots)
*max_slots = max_len;
max_len = 0;
@@ -170,12 +180,7 @@
struct f2fs_dir_entry *de = NULL;
bool room = false;
int max_slots;
- f2fs_hash_t namehash;
-
- if(fname->hash)
- namehash = cpu_to_le32(fname->hash);
- else
- namehash = f2fs_dentry_hash(&name);
+ f2fs_hash_t namehash = f2fs_dentry_hash(&name, fname);
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level);
@@ -539,7 +544,7 @@
level = 0;
slots = GET_DENTRY_SLOTS(new_name->len);
- dentry_hash = f2fs_dentry_hash(new_name);
+ dentry_hash = f2fs_dentry_hash(new_name, NULL);
current_depth = F2FS_I(dir)->i_current_depth;
if (F2FS_I(dir)->chash == dentry_hash) {
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 3a1640b..c12f695 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2016,7 +2016,8 @@
/*
* hash.c
*/
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *);
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct fscrypt_name *fname);
/*
* node.c
diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c
index 71b7206..eb2e031 100644
--- a/fs/f2fs/hash.c
+++ b/fs/f2fs/hash.c
@@ -70,7 +70,8 @@
*buf++ = pad;
}
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct fscrypt_name *fname)
{
__u32 hash;
f2fs_hash_t f2fs_hash;
@@ -79,6 +80,10 @@
const unsigned char *name = name_info->name;
size_t len = name_info->len;
+ /* encrypted bigname case */
+ if (fname && !fname->disk_name.name)
+ return cpu_to_le32(fname->hash);
+
if (is_dot_dotdot(name_info))
return 0;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 1427db9..e14edc9 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -312,7 +312,7 @@
return NULL;
}
- namehash = f2fs_dentry_hash(&name);
+ namehash = f2fs_dentry_hash(&name, fname);
inline_dentry = inline_data_addr(ipage);
@@ -549,7 +549,7 @@
f2fs_wait_on_page_writeback(ipage, NODE, true);
- name_hash = f2fs_dentry_hash(new_name);
+ name_hash = f2fs_dentry_hash(new_name, NULL);
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4e894d3..fc9b049 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2385,8 +2385,10 @@
if (status != 0)
return status;
}
- if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
+ if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) {
+ nfs4_sequence_free_slot(&o_res->seq_res);
nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label);
+ }
return 0;
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 965db47..142a74f 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -29,13 +29,14 @@
static struct kmem_cache *nfs_page_cachep;
static const struct rpc_call_ops nfs_pgio_common_ops;
-static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount)
+static bool nfs_pgarray_set(struct nfs_page_array *p, unsigned int pagecount,
+ gfp_t gfp_flags)
{
p->npages = pagecount;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
- p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL);
+ p->pagevec = kcalloc(pagecount, sizeof(struct page *), gfp_flags);
if (!p->pagevec)
p->npages = 0;
}
@@ -681,6 +682,7 @@
{
struct nfs_pgio_mirror *new;
int i;
+ gfp_t gfp_flags = GFP_KERNEL;
desc->pg_moreio = 0;
desc->pg_inode = inode;
@@ -700,8 +702,10 @@
if (pg_ops->pg_get_mirror_count) {
/* until we have a request, we don't have an lseg and no
* idea how many mirrors there will be */
+ if (desc->pg_rw_ops->rw_mode == FMODE_WRITE)
+ gfp_flags = GFP_NOIO;
new = kcalloc(NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX,
- sizeof(struct nfs_pgio_mirror), GFP_KERNEL);
+ sizeof(struct nfs_pgio_mirror), gfp_flags);
desc->pg_mirrors_dynamic = new;
desc->pg_mirrors = new;
@@ -755,9 +759,12 @@
struct list_head *head = &mirror->pg_list;
struct nfs_commit_info cinfo;
unsigned int pagecount, pageused;
+ gfp_t gfp_flags = GFP_KERNEL;
pagecount = nfs_page_array_len(mirror->pg_base, mirror->pg_count);
- if (!nfs_pgarray_set(&hdr->page_array, pagecount)) {
+ if (desc->pg_rw_ops->rw_mode == FMODE_WRITE)
+ gfp_flags = GFP_NOIO;
+ if (!nfs_pgarray_set(&hdr->page_array, pagecount, gfp_flags)) {
nfs_pgio_error(hdr);
desc->pg_error = -ENOMEM;
return desc->pg_error;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 5321183..e4772a8 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -548,9 +548,9 @@
{
nfs_unlock_request(req);
nfs_end_page_writeback(req);
- nfs_release_request(req);
generic_error_remove_page(page_file_mapping(req->wb_page),
req->wb_page);
+ nfs_release_request(req);
}
/*
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index abb09b5..650226f 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1273,7 +1273,8 @@
return NULL;
}
- if (!(exp->ex_layout_types & (1 << layout_type))) {
+ if (layout_type >= LAYOUT_TYPE_MAX ||
+ !(exp->ex_layout_types & (1 << layout_type))) {
dprintk("%s: layout type %d not supported\n",
__func__, layout_type);
return NULL;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index c2d2895..2ee80e1 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4081,8 +4081,7 @@
struct nfsd4_getdeviceinfo *gdev)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[gdev->gd_layout_type];
+ const struct nfsd4_layout_ops *ops;
u32 starting_len = xdr->buf->len, needed_len;
__be32 *p;
@@ -4099,6 +4098,7 @@
/* If maxcount is 0 then just update notifications */
if (gdev->gd_maxcount != 0) {
+ ops = nfsd4_layout_ops[gdev->gd_layout_type];
nfserr = ops->encode_getdeviceinfo(xdr, gdev);
if (nfserr) {
/*
@@ -4151,8 +4151,7 @@
struct nfsd4_layoutget *lgp)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[lgp->lg_layout_type];
+ const struct nfsd4_layout_ops *ops;
__be32 *p;
dprintk("%s: err %d\n", __func__, nfserr);
@@ -4175,6 +4174,7 @@
*p++ = cpu_to_be32(lgp->lg_seg.iomode);
*p++ = cpu_to_be32(lgp->lg_layout_type);
+ ops = nfsd4_layout_ops[lgp->lg_layout_type];
nfserr = ops->encode_layoutget(xdr, lgp);
out:
kfree(lgp->lg_content);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 7f99c96..cef9885 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -294,27 +294,37 @@
}
ret = copy_event_to_user(group, kevent, buf);
+ if (unlikely(ret == -EOPENSTALE)) {
+ /*
+ * We cannot report events with stale fd so drop it.
+ * Setting ret to 0 will continue the event loop and
+ * do the right thing if there are no more events to
+ * read (i.e. return bytes read, -EAGAIN or wait).
+ */
+ ret = 0;
+ }
+
/*
* Permission events get queued to wait for response. Other
* events can be destroyed now.
*/
if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) {
fsnotify_destroy_event(group, kevent);
- if (ret < 0)
- break;
} else {
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
- if (ret < 0) {
+ if (ret <= 0) {
FANOTIFY_PE(kevent)->response = FAN_DENY;
wake_up(&group->fanotify_data.access_waitq);
- break;
+ } else {
+ spin_lock(&group->notification_lock);
+ list_add_tail(&kevent->list,
+ &group->fanotify_data.access_list);
+ spin_unlock(&group->notification_lock);
}
- spin_lock(&group->notification_lock);
- list_add_tail(&kevent->list,
- &group->fanotify_data.access_list);
- spin_unlock(&group->notification_lock);
#endif
}
+ if (ret < 0)
+ break;
buf += ret;
count -= ret;
}
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 5f2dc20..6047471 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -471,6 +471,7 @@
ent->data = NULL;
ent->proc_fops = NULL;
ent->proc_iops = NULL;
+ parent->nlink++;
if (proc_register(parent, ent) < 0) {
kfree(ent);
parent->nlink--;
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index a231681..e9426a6 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -109,14 +109,16 @@
goto out;
/* If our top's inode is gone, we may be out of date */
- inode = d_inode(dentry);
+ inode = igrab(d_inode(dentry));
if (inode) {
data = top_data_get(SDCARDFS_I(inode));
- if (data->abandoned) {
+ if (!data || data->abandoned) {
d_drop(dentry);
err = 0;
}
- data_put(data);
+ if (data)
+ data_put(data);
+ iput(inode);
}
out:
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 9701f2d..a5696c1 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -144,6 +144,7 @@
struct drm_crtc *ptr;
struct drm_crtc_state *state;
struct drm_crtc_commit *commit;
+ s32 __user *out_fence_ptr;
};
struct __drm_connnectors_state {
@@ -316,6 +317,8 @@
struct drm_crtc *crtc);
void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
struct drm_framebuffer *fb);
+void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state,
+ struct fence *fence);
int __must_check
drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
struct drm_crtc *crtc);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index ac9d7d8..1c12875 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -531,6 +531,20 @@
* @audio_latency: audio latency info from ELD, if found
* @null_edid_counter: track sinks that give us all zeros for the EDID
* @bad_edid_counter: track sinks that give us an EDID with invalid checksum
+ * @max_tmds_char: indicates the maximum TMDS Character Rate supported
+ * @scdc_present: when set the sink supports SCDC functionality
+ * @rr_capable: when set the sink is capable of initiating an SCDC read request
+ * @supports_scramble: when set the sink supports less than 340Mcsc scrambling
+ * @flags_3d: 3D view(s) supported by the sink, see drm_edid.h (DRM_EDID_3D_*)
+ * @pt_scan_info: PT scan info obtained from the VCDB of EDID
+ * @it_scan_info: IT scan info obtained from the VCDB of EDID
+ * @ce_scan_info: CE scan info obtained from the VCDB of EDID
+ * @hdr_eotf: Electro optical transfer function obtained from HDR block
+ * @hdr_metadata_type_one: Metadata type one obtained from HDR block
+ * @hdr_max_luminance: desired max luminance obtained from HDR block
+ * @hdr_avg_luminance: desired avg luminance obtained from HDR block
+ * @hdr_min_luminance: desired min luminance obtained from HDR block
+ * @hdr_supported: does the sink support HDR content
* @edid_corrupt: indicates whether the last read EDID was corrupt
* @debugfs_entry: debugfs directory for this connector
* @state: current atomic state for this connector
@@ -665,6 +679,22 @@
int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
unsigned bad_edid_counter;
+ /* EDID bits HDMI 2.0 */
+ int max_tmds_char; /* in Mcsc */
+ bool scdc_present;
+ bool rr_capable;
+ bool supports_scramble;
+ int flags_3d;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u32 hdr_eotf;
+ bool hdr_metadata_type_one;
+ u32 hdr_max_luminance;
+ u32 hdr_avg_luminance;
+ u32 hdr_min_luminance;
+ bool hdr_supported;
+
/* Flag for raw EDID header corruption - used in Displayport
* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
*/
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 0aa2925..f3d58c7 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -680,6 +680,35 @@
* context.
*/
struct drm_modeset_acquire_ctx *acquire_ctx;
+
+ /**
+ * @fence_context:
+ *
+ * timeline context used for fence operations.
+ */
+ unsigned int fence_context;
+
+ /**
+ * @fence_lock:
+ *
+ * spinlock to protect the fences in the fence_context.
+ */
+
+ spinlock_t fence_lock;
+ /**
+ * @fence_seqno:
+ *
+ * Seqno variable used as monotonic counter for the fences
+ * created on the CRTC's timeline.
+ */
+ unsigned long fence_seqno;
+
+ /**
+ * @timeline_name:
+ *
+ * The name of the CRTC's fence timeline.
+ */
+ char timeline_name[32];
};
/**
@@ -1160,6 +1189,17 @@
*/
struct drm_property *prop_fb_id;
/**
+ * @prop_in_fence_fd: Sync File fd representing the incoming fences
+ * for a Plane.
+ */
+ struct drm_property *prop_in_fence_fd;
+ /**
+ * @prop_out_fence_ptr: Sync File fd pointer representing the
+ * outgoing fences for a CRTC. Userspace should provide a pointer to a
+ * value of type s32, and then cast that pointer to u64.
+ */
+ struct drm_property *prop_out_fence_ptr;
+ /**
* @prop_crtc_id: Default atomic plane property to specify the
* &drm_crtc.
*/
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index c3a7d44..32bd104 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -269,6 +269,11 @@
#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad))
+/* HDMI 2.0 */
+#define DRM_EDID_3D_INDEPENDENT_VIEW (1 << 2)
+#define DRM_EDID_3D_DUAL_VIEW (1 << 1)
+#define DRM_EDID_3D_OSD_DISPARITY (1 << 0)
+
struct edid {
u8 header[8];
/* Vendor & product info */
diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h
index f313211..3b00f64 100644
--- a/include/drm/drm_fb_cma_helper.h
+++ b/include/drm/drm_fb_cma_helper.h
@@ -12,6 +12,8 @@
struct drm_device;
struct drm_file;
struct drm_mode_fb_cmd2;
+struct drm_plane;
+struct drm_plane_state;
struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
@@ -41,6 +43,9 @@
struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
unsigned int plane);
+int drm_fb_cma_prepare_fb(struct drm_plane *plane,
+ struct drm_plane_state *state);
+
#ifdef CONFIG_DEBUG_FS
struct seq_file;
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 8b4dc62..952ef84 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -65,7 +65,7 @@
struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_plane() */
struct drm_framebuffer *fb; /* do not write directly, use drm_atomic_set_fb_for_plane() */
- struct fence *fence;
+ struct fence *fence; /* do not write directly, use drm_atomic_set_fence_for_plane() */
/* Signed dest location allows it to be partially off screen */
int32_t crtc_x, crtc_y;
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
index 915ac08..e773848 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -83,19 +83,20 @@
#define GCC_SPMI_FETCHER_CLK 65
#define GCC_SPMI_FETCHER_CLK_SRC 66
#define GCC_SYS_NOC_CPUSS_AHB_CLK 67
-#define GCC_USB30_MASTER_CLK 68
-#define GCC_USB30_MASTER_CLK_SRC 69
-#define GCC_USB30_MOCK_UTMI_CLK 70
-#define GCC_USB30_MOCK_UTMI_CLK_SRC 71
-#define GCC_USB30_SLEEP_CLK 72
-#define GCC_USB3_PRIM_CLKREF_CLK 73
-#define GCC_USB3_PHY_AUX_CLK 74
-#define GCC_USB3_PHY_AUX_CLK_SRC 75
-#define GCC_USB3_PHY_PIPE_CLK 76
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 77
-#define GCC_XO_DIV4_CLK 78
-#define GPLL0 79
-#define GPLL0_OUT_EVEN 80
+#define GCC_SYS_NOC_USB3_CLK 68
+#define GCC_USB30_MASTER_CLK 69
+#define GCC_USB30_MASTER_CLK_SRC 70
+#define GCC_USB30_MOCK_UTMI_CLK 71
+#define GCC_USB30_MOCK_UTMI_CLK_SRC 72
+#define GCC_USB30_SLEEP_CLK 73
+#define GCC_USB3_PRIM_CLKREF_CLK 74
+#define GCC_USB3_PHY_AUX_CLK 75
+#define GCC_USB3_PHY_AUX_CLK_SRC 76
+#define GCC_USB3_PHY_PIPE_CLK 77
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 78
+#define GCC_XO_DIV4_CLK 79
+#define GPLL0 80
+#define GPLL0_OUT_EVEN 81
/* GDSCs */
#define PCIE_GDSC 0
diff --git a/include/dt-bindings/clock/qcom,gpucc-sdm845.h b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
index c43a9f8..323beaf 100644
--- a/include/dt-bindings/clock/qcom,gpucc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
@@ -40,13 +40,10 @@
#define GPU_CC_PLL1_OUT_ODD 22
#define GPU_CC_PLL1_OUT_TEST 23
#define GPU_CC_PLL_TEST_CLK 24
-#define GPU_CC_RBCPR_AHB_CLK 25
-#define GPU_CC_RBCPR_CLK 26
-#define GPU_CC_RBCPR_CLK_SRC 27
-#define GPU_CC_SLEEP_CLK 28
-#define GPU_CC_GMU_CLK_SRC 29
-#define GPU_CC_CX_GFX3D_CLK 30
-#define GPU_CC_CX_GFX3D_SLV_CLK 31
+#define GPU_CC_SLEEP_CLK 25
+#define GPU_CC_GMU_CLK_SRC 26
+#define GPU_CC_CX_GFX3D_CLK 27
+#define GPU_CC_CX_GFX3D_SLV_CLK 28
/* GPUCC reset clock registers */
#define GPUCC_GPU_CC_ACD_BCR 0
@@ -54,9 +51,8 @@
#define GPUCC_GPU_CC_GFX3D_AON_BCR 2
#define GPUCC_GPU_CC_GMU_BCR 3
#define GPUCC_GPU_CC_GX_BCR 4
-#define GPUCC_GPU_CC_RBCPR_BCR 5
-#define GPUCC_GPU_CC_SPDM_BCR 6
-#define GPUCC_GPU_CC_XO_BCR 7
+#define GPUCC_GPU_CC_SPDM_BCR 5
+#define GPUCC_GPU_CC_XO_BCR 6
/* GFX3D clock registers */
#define GPU_CC_PLL0 0
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
new file mode 100644
index 0000000..ec80d0c
--- /dev/null
+++ b/include/linux/bpf-cgroup.h
@@ -0,0 +1,79 @@
+#ifndef _BPF_CGROUP_H
+#define _BPF_CGROUP_H
+
+#include <linux/bpf.h>
+#include <linux/jump_label.h>
+#include <uapi/linux/bpf.h>
+
+struct sock;
+struct cgroup;
+struct sk_buff;
+
+#ifdef CONFIG_CGROUP_BPF
+
+extern struct static_key_false cgroup_bpf_enabled_key;
+#define cgroup_bpf_enabled static_branch_unlikely(&cgroup_bpf_enabled_key)
+
+struct cgroup_bpf {
+ /*
+ * Store two sets of bpf_prog pointers, one for programs that are
+ * pinned directly to this cgroup, and one for those that are effective
+ * when this cgroup is accessed.
+ */
+ struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE];
+ struct bpf_prog *effective[MAX_BPF_ATTACH_TYPE];
+};
+
+void cgroup_bpf_put(struct cgroup *cgrp);
+void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent);
+
+void __cgroup_bpf_update(struct cgroup *cgrp,
+ struct cgroup *parent,
+ struct bpf_prog *prog,
+ enum bpf_attach_type type);
+
+/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */
+void cgroup_bpf_update(struct cgroup *cgrp,
+ struct bpf_prog *prog,
+ enum bpf_attach_type type);
+
+int __cgroup_bpf_run_filter(struct sock *sk,
+ struct sk_buff *skb,
+ enum bpf_attach_type type);
+
+/* Wrappers for __cgroup_bpf_run_filter() guarded by cgroup_bpf_enabled. */
+#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) \
+({ \
+ int __ret = 0; \
+ if (cgroup_bpf_enabled) \
+ __ret = __cgroup_bpf_run_filter(sk, skb, \
+ BPF_CGROUP_INET_INGRESS); \
+ \
+ __ret; \
+})
+
+#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) \
+({ \
+ int __ret = 0; \
+ if (cgroup_bpf_enabled && sk && sk == skb->sk) { \
+ typeof(sk) __sk = sk_to_full_sk(sk); \
+ if (sk_fullsock(__sk)) \
+ __ret = __cgroup_bpf_run_filter(__sk, skb, \
+ BPF_CGROUP_INET_EGRESS); \
+ } \
+ __ret; \
+})
+
+#else
+
+struct cgroup_bpf {};
+static inline void cgroup_bpf_put(struct cgroup *cgrp) {}
+static inline void cgroup_bpf_inherit(struct cgroup *cgrp,
+ struct cgroup *parent) {}
+
+#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; })
+
+#endif /* CONFIG_CGROUP_BPF */
+
+#endif /* _BPF_CGROUP_H */
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 0f4548c..b008a33 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -16,6 +16,7 @@
#include <linux/percpu-refcount.h>
#include <linux/percpu-rwsem.h>
#include <linux/workqueue.h>
+#include <linux/bpf-cgroup.h>
#ifdef CONFIG_CGROUPS
@@ -300,6 +301,9 @@
/* used to schedule release agent */
struct work_struct release_agent_work;
+ /* used to store eBPF programs */
+ struct cgroup_bpf bpf;
+
/* ids of the ancestors at each level including self */
int ancestor_ids[];
};
diff --git a/include/linux/fence.h b/include/linux/fence.h
index 8d7265f..fd9b89f 100644
--- a/include/linux/fence.h
+++ b/include/linux/fence.h
@@ -185,6 +185,16 @@
void fence_free(struct fence *fence);
/**
+ * fence_put - decreases refcount of the fence
+ * @fence: [in] fence to reduce refcount of
+ */
+static inline void fence_put(struct fence *fence)
+{
+ if (fence)
+ kref_put(&fence->refcount, fence_release);
+}
+
+/**
* fence_get - increases refcount of the fence
* @fence: [in] fence to increase refcount of
*
@@ -212,13 +222,49 @@
}
/**
- * fence_put - decreases refcount of the fence
- * @fence: [in] fence to reduce refcount of
+ * fence_get_rcu_safe - acquire a reference to an RCU tracked fence
+ * @fence: [in] pointer to fence to increase refcount of
+ *
+ * Function returns NULL if no refcount could be obtained, or the fence.
+ * This function handles acquiring a reference to a fence that may be
+ * reallocated within the RCU grace period (such as with SLAB_DESTROY_BY_RCU),
+ * so long as the caller is using RCU on the pointer to the fence.
+ *
+ * An alternative mechanism is to employ a seqlock to protect a bunch of
+ * fences, such as used by struct reservation_object. When using a seqlock,
+ * the seqlock must be taken before and checked after a reference to the
+ * fence is acquired (as shown here).
+ *
+ * The caller is required to hold the RCU read lock.
*/
-static inline void fence_put(struct fence *fence)
+static inline struct fence *fence_get_rcu_safe(struct fence * __rcu *fencep)
{
- if (fence)
- kref_put(&fence->refcount, fence_release);
+ do {
+ struct fence *fence;
+
+ fence = rcu_dereference(*fencep);
+ if (!fence || !fence_get_rcu(fence))
+ return NULL;
+
+ /* The atomic_inc_not_zero() inside fence_get_rcu()
+ * provides a full memory barrier upon success (such as now).
+ * This is paired with the write barrier from assigning
+ * to the __rcu protected fence pointer so that if that
+ * pointer still matches the current fence, we know we
+ * have successfully acquire a reference to it. If it no
+ * longer matches, we are holding a reference to some other
+ * reallocated pointer. This is possible if the allocator
+ * is using a freelist like SLAB_DESTROY_BY_RCU where the
+ * fence remains valid for the RCU grace period, but it
+ * may be reallocated. When using such allocators, we are
+ * responsible for ensuring the reference we get is to
+ * the right fence, as below.
+ */
+ if (fence == rcu_access_pointer(*fencep))
+ return rcu_pointer_handoff(fence);
+
+ fence_put(fence);
+ } while (1);
}
int fence_signal(struct fence *fence);
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index e974420..bc38b99a 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -35,6 +35,7 @@
};
#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_IEEE_OUI_HF 0xc45dd8
#define HDMI_INFOFRAME_HEADER_SIZE 4
#define HDMI_AVI_INFOFRAME_SIZE 13
#define HDMI_SPD_INFOFRAME_SIZE 25
@@ -78,6 +79,8 @@
HDMI_PICTURE_ASPECT_NONE,
HDMI_PICTURE_ASPECT_4_3,
HDMI_PICTURE_ASPECT_16_9,
+ HDMI_PICTURE_ASPECT_64_27,
+ HDMI_PICTURE_ASPECT_256_135,
HDMI_PICTURE_ASPECT_RESERVED,
};
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
index dd85f35..039e6ab 100644
--- a/include/linux/hid-sensor-hub.h
+++ b/include/linux/hid-sensor-hub.h
@@ -231,6 +231,8 @@
unsigned usage_id;
atomic_t data_ready;
atomic_t user_requested_state;
+ int poll_interval;
+ int raw_hystersis;
struct iio_trigger *trigger;
struct hid_sensor_hub_attribute_info poll;
struct hid_sensor_hub_attribute_info report_state;
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 8f68490..e233925 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -330,7 +330,9 @@
int write, void __user *buffer,
size_t *length, loff_t *ppos);
#endif
-
+extern void wait_for_kprobe_optimizer(void);
+#else
+static inline void wait_for_kprobe_optimizer(void) { }
#endif /* CONFIG_OPTPROBES */
#ifdef CONFIG_KPROBES_ON_FTRACE
extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
diff --git a/include/linux/nfcinfo.h b/include/linux/nfcinfo.h
new file mode 100644
index 0000000..b67a65f
--- /dev/null
+++ b/include/linux/nfcinfo.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2016-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.
+ */
+
+#ifndef _NFCINFO_H
+#define _NFCINFO_H
+
+#include <uapi/linux/nfc/nfcinfo.h>
+
+#endif
diff --git a/include/linux/reservation.h b/include/linux/reservation.h
index b0f305e..bad7710 100644
--- a/include/linux/reservation.h
+++ b/include/linux/reservation.h
@@ -177,17 +177,14 @@
reservation_object_get_excl_rcu(struct reservation_object *obj)
{
struct fence *fence;
- unsigned seq;
-retry:
- seq = read_seqcount_begin(&obj->seq);
+
+ if (!rcu_access_pointer(obj->fence_excl))
+ return NULL;
+
rcu_read_lock();
- fence = rcu_dereference(obj->fence_excl);
- if (read_seqcount_retry(&obj->seq, seq)) {
- rcu_read_unlock();
- goto retry;
- }
- fence = fence_get(fence);
+ fence = fence_get_rcu_safe(&obj->fence_excl);
rcu_read_unlock();
+
return fence;
}
diff --git a/include/linux/sde_rsc.h b/include/linux/sde_rsc.h
index f3fa9e6..f921909 100644
--- a/include/linux/sde_rsc.h
+++ b/include/linux/sde_rsc.h
@@ -206,6 +206,23 @@
*/
void sde_rsc_unregister_event(struct sde_rsc_event *event);
+/**
+ * is_sde_rsc_available - check if display rsc available.
+ * @rsc_index: A client will be created on this RSC. As of now only
+ * SDE_RSC_INDEX is valid rsc index.
+ * Returns: true if rsc is available; false in all other cases
+ */
+bool is_sde_rsc_available(int rsc_index);
+
+/**
+ * get_sde_rsc_current_state - gets the current state of sde rsc.
+ * @rsc_index: A client will be created on this RSC. As of now only
+ * SDE_RSC_INDEX is valid rsc index.
+ * Returns: current state if rsc available; SDE_RSC_IDLE_STATE for
+ * all other cases
+ */
+enum sde_rsc_state get_sde_rsc_current_state(int rsc_index);
+
#else
static inline struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index,
@@ -242,6 +259,15 @@
{
}
+static inline bool is_sde_rsc_available(int rsc_index)
+{
+ return false;
+}
+
+static inline enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
+{
+ return SDE_RSC_IDLE_STATE;
+}
#endif /* CONFIG_DRM_SDE_RSC */
#endif /* _SDE_RSC_H_ */
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
index a0596ca0..a2f8109 100644
--- a/include/linux/sock_diag.h
+++ b/include/linux/sock_diag.h
@@ -24,6 +24,7 @@
void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
+u64 sock_gen_cookie(struct sock *sk);
int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie);
void sock_diag_save_cookie(struct sock *sk, __u32 *cookie);
diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h
index 5478417..9a0a53e 100644
--- a/include/soc/qcom/subsystem_restart.h
+++ b/include/soc/qcom/subsystem_restart.h
@@ -56,6 +56,8 @@
* @sysmon_shutdown_ret: Return value for the call to sysmon_send_shutdown
* @system_debug: If "set", triggers a device restart when the
* subsystem's wdog bite handler is invoked.
+ * @ignore_ssr_failure: SSR failures are usually fatal and results in panic. If
+ * set will ignore failure.
* @edge: GLINK logical name of the subsystem
*/
struct subsys_desc {
@@ -91,6 +93,7 @@
u32 sysmon_pid;
int sysmon_shutdown_ret;
bool system_debug;
+ bool ignore_ssr_failure;
const char *edge;
};
diff --git a/include/trace/events/pdc.h b/include/trace/events/pdc.h
new file mode 100644
index 0000000..400e959
--- /dev/null
+++ b/include/trace/events/pdc.h
@@ -0,0 +1,50 @@
+/* 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM pdc
+
+#if !defined(_TRACE_PDC_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_PDC_H_
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(irq_pin_config,
+
+ TP_PROTO(char *func, u32 pin, u32 hwirq, u32 type, u32 enable),
+
+ TP_ARGS(func, pin, hwirq, type, enable),
+
+ TP_STRUCT__entry(
+ __field(char *, func)
+ __field(u32, pin)
+ __field(u32, hwirq)
+ __field(u32, type)
+ __field(u32, enable)
+ ),
+
+ TP_fast_assign(
+ __entry->pin = pin;
+ __entry->func = func;
+ __entry->hwirq = hwirq;
+ __entry->type = type;
+ __entry->enable = enable;
+ ),
+
+ TP_printk("%s hwirq:%u pin:%u type:%u enable:%u",
+ __entry->func, __entry->pin, __entry->hwirq, __entry->type,
+ __entry->enable)
+);
+
+#endif
+#define TRACE_INCLUDE_FILE pdc
+#include <trace/define_trace.h>
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index f66ba9c..9c927a5 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -77,7 +77,8 @@
#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14)
#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14)
#define DRM_MODE_FLAG_SEAMLESS (1<<19)
-
+#define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20)
+#define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21)
/* DPMS flags */
/* bit compatible with the xorg definitions. */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 7cf7779..cd758c2 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -525,3 +525,4 @@
header-y += msm_dsps.h
header-y += msm-core-interface.h
header-y += msm_rotator.h
+header-y += nfc/
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f09c70b..14eaf2d 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -73,6 +73,8 @@
BPF_PROG_LOAD,
BPF_OBJ_PIN,
BPF_OBJ_GET,
+ BPF_PROG_ATTACH,
+ BPF_PROG_DETACH,
};
enum bpf_map_type {
@@ -96,8 +98,17 @@
BPF_PROG_TYPE_TRACEPOINT,
BPF_PROG_TYPE_XDP,
BPF_PROG_TYPE_PERF_EVENT,
+ BPF_PROG_TYPE_CGROUP_SKB,
};
+enum bpf_attach_type {
+ BPF_CGROUP_INET_INGRESS,
+ BPF_CGROUP_INET_EGRESS,
+ __MAX_BPF_ATTACH_TYPE
+};
+
+#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
+
#define BPF_PSEUDO_MAP_FD 1
/* flags for BPF_MAP_UPDATE_ELEM command */
@@ -141,6 +152,12 @@
__aligned_u64 pathname;
__u32 bpf_fd;
};
+
+ struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
+ __u32 target_fd; /* container object to attach to */
+ __u32 attach_bpf_fd; /* eBPF program to attach */
+ __u32 attach_type;
+ };
} __attribute__((aligned(8)));
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -426,6 +443,67 @@
*/
BPF_FUNC_set_hash_invalid,
+ /**
+ * int bpf_get_numa_node_id()
+ * Return: Id of current NUMA node.
+ */
+ BPF_FUNC_get_numa_node_id,
+
+ /**
+ * int bpf_skb_change_head()
+ * Grows headroom of skb and adjusts MAC header offset accordingly.
+ * Will extends/reallocae as required automatically.
+ * May change skb data pointer and will thus invalidate any check
+ * performed for direct packet access.
+ * @skb: pointer to skb
+ * @len: length of header to be pushed in front
+ * @flags: Flags (unused for now)
+ * Return: 0 on success or negative error
+ */
+ BPF_FUNC_skb_change_head,
+
+ /**
+ * int bpf_xdp_adjust_head(xdp_md, delta)
+ * Adjust the xdp_md.data by delta
+ * @xdp_md: pointer to xdp_md
+ * @delta: An positive/negative integer to be added to xdp_md.data
+ * Return: 0 on success or negative on error
+ */
+ BPF_FUNC_xdp_adjust_head,
+
+ /**
+ * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
+ * Copy a NUL terminated string from unsafe address. In case the string
+ * length is smaller than size, the target is not padded with further NUL
+ * bytes. In case the string length is larger than size, just count-1
+ * bytes are copied and the last byte is set to NUL.
+ * @dst: destination address
+ * @size: maximum number of bytes to copy, including the trailing NUL
+ * @unsafe_ptr: unsafe address
+ * Return:
+ * > 0 length of the string including the trailing NUL on success
+ * < 0 error
+ */
+ BPF_FUNC_probe_read_str,
+
+ /**
+ * u64 bpf_bpf_get_socket_cookie(skb)
+ * Get the cookie for the socket stored inside sk_buff.
+ * @skb: pointer to skb
+ * Return: 8 Bytes non-decreasing number on success or 0 if the socket
+ * field is missing inside sk_buff
+ */
+ BPF_FUNC_get_socket_cookie,
+
+ /**
+ * u32 bpf_get_socket_uid(skb)
+ * Get the owner uid of the socket stored inside sk_buff.
+ * @skb: pointer to skb
+ * Return: uid of the socket owner on success or 0 if the socket pointer
+ * inside sk_buff is NULL
+ */
+ BPF_FUNC_get_socket_uid,
+
__BPF_FUNC_MAX_ID,
};
diff --git a/include/uapi/linux/nfc/Kbuild b/include/uapi/linux/nfc/Kbuild
new file mode 100644
index 0000000..9071015
--- /dev/null
+++ b/include/uapi/linux/nfc/Kbuild
@@ -0,0 +1,2 @@
+#UAPI export list
+header-y += nfcinfo.h
diff --git a/include/uapi/linux/nfc/nfcinfo.h b/include/uapi/linux/nfc/nfcinfo.h
new file mode 100644
index 0000000..df178e2
--- /dev/null
+++ b/include/uapi/linux/nfc/nfcinfo.h
@@ -0,0 +1,21 @@
+#ifndef _UAPI_NFCINFO_H_
+#define _UAPI_NFCINFO_H_
+
+#include <linux/ioctl.h>
+
+#define NFCC_MAGIC 0xE9
+#define NFCC_GET_INFO _IOW(NFCC_MAGIC, 0x09, unsigned int)
+
+struct nqx_devinfo {
+ unsigned char chip_type;
+ unsigned char rom_version;
+ unsigned char fw_major;
+ unsigned char fw_minor;
+};
+
+union nqx_uinfo {
+ unsigned int i;
+ struct nqx_devinfo info;
+};
+
+#endif
diff --git a/init/Kconfig b/init/Kconfig
index bdfcc0f..d8a5868 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1248,6 +1248,19 @@
Say N if unsure.
+config CGROUP_BPF
+ bool "Support for eBPF programs attached to cgroups"
+ depends on BPF_SYSCALL
+ select SOCK_CGROUP_DATA
+ help
+ Allow attaching eBPF programs to a cgroup using the bpf(2)
+ syscall command BPF_PROG_ATTACH.
+
+ In which context these programs are accessed depends on the type
+ of attachment. For instance, programs that are attached using
+ BPF_CGROUP_INET_INGRESS will be executed on the ingress path of
+ inet sockets.
+
config CGROUP_DEBUG
bool "Example controller"
default n
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index eed911d..b22256b 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -5,3 +5,4 @@
ifeq ($(CONFIG_PERF_EVENTS),y)
obj-$(CONFIG_BPF_SYSCALL) += stackmap.o
endif
+obj-$(CONFIG_CGROUP_BPF) += cgroup.o
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
new file mode 100644
index 0000000..d05c292
--- /dev/null
+++ b/kernel/bpf/cgroup.c
@@ -0,0 +1,170 @@
+/*
+ * Functions to manage eBPF programs attached to cgroups
+ *
+ * Copyright (c) 2016 Daniel Mack
+ *
+ * This file is subject to the terms and conditions of version 2 of the GNU
+ * General Public License. See the file COPYING in the main directory of the
+ * Linux distribution for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/atomic.h>
+#include <linux/cgroup.h>
+#include <linux/slab.h>
+#include <linux/bpf.h>
+#include <linux/bpf-cgroup.h>
+#include <net/sock.h>
+
+DEFINE_STATIC_KEY_FALSE(cgroup_bpf_enabled_key);
+EXPORT_SYMBOL(cgroup_bpf_enabled_key);
+
+/**
+ * cgroup_bpf_put() - put references of all bpf programs
+ * @cgrp: the cgroup to modify
+ */
+void cgroup_bpf_put(struct cgroup *cgrp)
+{
+ unsigned int type;
+
+ for (type = 0; type < ARRAY_SIZE(cgrp->bpf.prog); type++) {
+ struct bpf_prog *prog = cgrp->bpf.prog[type];
+
+ if (prog) {
+ bpf_prog_put(prog);
+ static_branch_dec(&cgroup_bpf_enabled_key);
+ }
+ }
+}
+
+/**
+ * cgroup_bpf_inherit() - inherit effective programs from parent
+ * @cgrp: the cgroup to modify
+ * @parent: the parent to inherit from
+ */
+void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent)
+{
+ unsigned int type;
+
+ for (type = 0; type < ARRAY_SIZE(cgrp->bpf.effective); type++) {
+ struct bpf_prog *e;
+
+ e = rcu_dereference_protected(parent->bpf.effective[type],
+ lockdep_is_held(&cgroup_mutex));
+ rcu_assign_pointer(cgrp->bpf.effective[type], e);
+ }
+}
+
+/**
+ * __cgroup_bpf_update() - Update the pinned program of a cgroup, and
+ * propagate the change to descendants
+ * @cgrp: The cgroup which descendants to traverse
+ * @parent: The parent of @cgrp, or %NULL if @cgrp is the root
+ * @prog: A new program to pin
+ * @type: Type of pinning operation (ingress/egress)
+ *
+ * Each cgroup has a set of two pointers for bpf programs; one for eBPF
+ * programs it owns, and which is effective for execution.
+ *
+ * If @prog is %NULL, this function attaches a new program to the cgroup and
+ * releases the one that is currently attached, if any. @prog is then made
+ * the effective program of type @type in that cgroup.
+ *
+ * If @prog is %NULL, the currently attached program of type @type is released,
+ * and the effective program of the parent cgroup (if any) is inherited to
+ * @cgrp.
+ *
+ * Then, the descendants of @cgrp are walked and the effective program for
+ * each of them is set to the effective program of @cgrp unless the
+ * descendant has its own program attached, in which case the subbranch is
+ * skipped. This ensures that delegated subcgroups with own programs are left
+ * untouched.
+ *
+ * Must be called with cgroup_mutex held.
+ */
+void __cgroup_bpf_update(struct cgroup *cgrp,
+ struct cgroup *parent,
+ struct bpf_prog *prog,
+ enum bpf_attach_type type)
+{
+ struct bpf_prog *old_prog, *effective;
+ struct cgroup_subsys_state *pos;
+
+ old_prog = xchg(cgrp->bpf.prog + type, prog);
+
+ effective = (!prog && parent) ?
+ rcu_dereference_protected(parent->bpf.effective[type],
+ lockdep_is_held(&cgroup_mutex)) :
+ prog;
+
+ css_for_each_descendant_pre(pos, &cgrp->self) {
+ struct cgroup *desc = container_of(pos, struct cgroup, self);
+
+ /* skip the subtree if the descendant has its own program */
+ if (desc->bpf.prog[type] && desc != cgrp)
+ pos = css_rightmost_descendant(pos);
+ else
+ rcu_assign_pointer(desc->bpf.effective[type],
+ effective);
+ }
+
+ if (prog)
+ static_branch_inc(&cgroup_bpf_enabled_key);
+
+ if (old_prog) {
+ bpf_prog_put(old_prog);
+ static_branch_dec(&cgroup_bpf_enabled_key);
+ }
+}
+
+/**
+ * __cgroup_bpf_run_filter() - Run a program for packet filtering
+ * @sk: The socket sending or receiving traffic
+ * @skb: The skb that is being sent or received
+ * @type: The type of program to be exectuted
+ *
+ * If no socket is passed, or the socket is not of type INET or INET6,
+ * this function does nothing and returns 0.
+ *
+ * The program type passed in via @type must be suitable for network
+ * filtering. No further check is performed to assert that.
+ *
+ * This function will return %-EPERM if any if an attached program was found
+ * and if it returned != 1 during execution. In all other cases, 0 is returned.
+ */
+int __cgroup_bpf_run_filter(struct sock *sk,
+ struct sk_buff *skb,
+ enum bpf_attach_type type)
+{
+ struct bpf_prog *prog;
+ struct cgroup *cgrp;
+ int ret = 0;
+
+ if (!sk || !sk_fullsock(sk))
+ return 0;
+
+ if (sk->sk_family != AF_INET &&
+ sk->sk_family != AF_INET6)
+ return 0;
+
+ cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
+
+ rcu_read_lock();
+
+ prog = rcu_dereference(cgrp->bpf.effective[type]);
+ if (prog) {
+ unsigned int offset = skb->data - skb_network_header(skb);
+ struct sock *save_sk = skb->sk;
+
+ skb->sk = sk;
+ __skb_push(skb, offset);
+ ret = bpf_prog_run_save_cb(prog, skb) == 1 ? 0 : -EPERM;
+ __skb_pull(skb, offset);
+ skb->sk = save_sk;
+ }
+
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(__cgroup_bpf_run_filter);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 237f3d6..e13157f 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -824,6 +824,77 @@
return bpf_obj_get_user(u64_to_ptr(attr->pathname));
}
+#ifdef CONFIG_CGROUP_BPF
+
+#define BPF_PROG_ATTACH_LAST_FIELD attach_type
+
+static int bpf_prog_attach(const union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ struct cgroup *cgrp;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (CHECK_ATTR(BPF_PROG_ATTACH))
+ return -EINVAL;
+
+ switch (attr->attach_type) {
+ case BPF_CGROUP_INET_INGRESS:
+ case BPF_CGROUP_INET_EGRESS:
+ prog = bpf_prog_get_type(attr->attach_bpf_fd,
+ BPF_PROG_TYPE_CGROUP_SKB);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ cgrp = cgroup_get_from_fd(attr->target_fd);
+ if (IS_ERR(cgrp)) {
+ bpf_prog_put(prog);
+ return PTR_ERR(cgrp);
+ }
+
+ cgroup_bpf_update(cgrp, prog, attr->attach_type);
+ cgroup_put(cgrp);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define BPF_PROG_DETACH_LAST_FIELD attach_type
+
+static int bpf_prog_detach(const union bpf_attr *attr)
+{
+ struct cgroup *cgrp;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (CHECK_ATTR(BPF_PROG_DETACH))
+ return -EINVAL;
+
+ switch (attr->attach_type) {
+ case BPF_CGROUP_INET_INGRESS:
+ case BPF_CGROUP_INET_EGRESS:
+ cgrp = cgroup_get_from_fd(attr->target_fd);
+ if (IS_ERR(cgrp))
+ return PTR_ERR(cgrp);
+
+ cgroup_bpf_update(cgrp, NULL, attr->attach_type);
+ cgroup_put(cgrp);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_CGROUP_BPF */
+
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
{
union bpf_attr attr = {};
@@ -890,6 +961,16 @@
case BPF_OBJ_GET:
err = bpf_obj_get(&attr);
break;
+
+#ifdef CONFIG_CGROUP_BPF
+ case BPF_PROG_ATTACH:
+ err = bpf_prog_attach(&attr);
+ break;
+ case BPF_PROG_DETACH:
+ err = bpf_prog_detach(&attr);
+ break;
+#endif
+
default:
err = -EINVAL;
break;
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 6670008..0fab276 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -5096,6 +5096,8 @@
if (cgrp->kn)
RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv,
NULL);
+
+ cgroup_bpf_put(cgrp);
}
mutex_unlock(&cgroup_mutex);
@@ -5308,6 +5310,9 @@
if (!cgroup_on_dfl(cgrp))
cgrp->subtree_control = cgroup_control(cgrp);
+ if (parent)
+ cgroup_bpf_inherit(cgrp, parent);
+
cgroup_propagate_control(cgrp);
return cgrp;
@@ -6514,6 +6519,19 @@
}
subsys_initcall(cgroup_namespaces_init);
+#ifdef CONFIG_CGROUP_BPF
+void cgroup_bpf_update(struct cgroup *cgrp,
+ struct bpf_prog *prog,
+ enum bpf_attach_type type)
+{
+ struct cgroup *parent = cgroup_parent(cgrp);
+
+ mutex_lock(&cgroup_mutex);
+ __cgroup_bpf_update(cgrp, parent, prog, type);
+ mutex_unlock(&cgroup_mutex);
+}
+#endif /* CONFIG_CGROUP_BPF */
+
#ifdef CONFIG_CGROUP_DEBUG
static struct cgroup_subsys_state *
debug_css_alloc(struct cgroup_subsys_state *parent_css)
diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config
index b7b997f..bc615c6 100644
--- a/kernel/configs/android-base.config
+++ b/kernel/configs/android-base.config
@@ -19,6 +19,7 @@
CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_DEFAULT_SECURITY_SELINUX=y
CONFIG_EMBEDDED=y
diff --git a/kernel/fork.c b/kernel/fork.c
index cb4faae..33663b0 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -521,7 +521,7 @@
set_task_stack_end_magic(tsk);
#ifdef CONFIG_CC_STACKPROTECTOR
- tsk->stack_canary = get_random_int();
+ tsk->stack_canary = get_random_long();
#endif
/*
@@ -1774,11 +1774,13 @@
*/
recalc_sigpending();
if (signal_pending(current)) {
- spin_unlock(¤t->sighand->siglock);
- write_unlock_irq(&tasklist_lock);
retval = -ERESTARTNOINTR;
goto bad_fork_cancel_cgroup;
}
+ if (unlikely(!(ns_of_pid(pid)->nr_hashed & PIDNS_HASH_ADDING))) {
+ retval = -ENOMEM;
+ goto bad_fork_cancel_cgroup;
+ }
if (likely(p->pid)) {
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
@@ -1829,6 +1831,8 @@
return p;
bad_fork_cancel_cgroup:
+ spin_unlock(¤t->sighand->siglock);
+ write_unlock_irq(&tasklist_lock);
cgroup_cancel_fork(p);
bad_fork_free_pid:
threadgroup_change_end(current);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index be3c34e..077c87f 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -877,8 +877,8 @@
if (!desc)
return;
- __irq_do_set_handler(desc, handle, 1, NULL);
desc->irq_common_data.handler_data = data;
+ __irq_do_set_handler(desc, handle, 1, NULL);
irq_put_desc_busunlock(desc, flags);
}
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index d630954..a1a07cf 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -563,7 +563,7 @@
}
/* Wait for completing optimization and unoptimization */
-static void wait_for_kprobe_optimizer(void)
+void wait_for_kprobe_optimizer(void)
{
mutex_lock(&kprobe_mutex);
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index a70b90d..c61c56f 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/debug_locks.h>
#include <linux/osq_lock.h>
+#include <linux/delay.h>
/*
* In the DEBUG case we are using the "NULL fastpath" for mutexes,
@@ -378,6 +379,17 @@
* values at the cost of a few extra spins.
*/
cpu_relax_lowlatency();
+
+ /*
+ * On arm systems, we must slow down the waiter's repeated
+ * aquisition of spin_mlock and atomics on the lock count, or
+ * we risk starving out a thread attempting to release the
+ * mutex. The mutex slowpath release must take spin lock
+ * wait_lock. This spin lock can share a monitor with the
+ * other waiter atomics in the mutex data structure, so must
+ * take care to rate limit the waiters.
+ */
+ udelay(1);
}
osq_unlock(&lock->osq);
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index eef2ce9..3976dd5 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -274,7 +274,7 @@
* if reparented.
*/
for (;;) {
- set_current_state(TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
if (pid_ns->nr_hashed == init_pids)
break;
schedule();
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 42630ec..c42380a 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -24,6 +24,7 @@
struct gov_attr_set attr_set;
unsigned int rate_limit_us;
unsigned int hispeed_freq;
+ bool pl;
};
struct sugov_policy {
@@ -224,7 +225,8 @@
if (is_hiload && nl >= mult_frac(cpu_util, NL_RATIO, 100))
*util = *max;
- *util = max(*util, sg_cpu->walt_load.pl);
+ if (sg_policy->tunables->pl)
+ *util = max(*util, sg_cpu->walt_load.pl);
}
static void sugov_update_single(struct update_util_data *hook, u64 time,
@@ -450,12 +452,32 @@
return count;
}
+static ssize_t pl_show(struct gov_attr_set *attr_set, char *buf)
+{
+ struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
+
+ return sprintf(buf, "%u\n", tunables->pl);
+}
+
+static ssize_t pl_store(struct gov_attr_set *attr_set, const char *buf,
+ size_t count)
+{
+ struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
+
+ if (kstrtobool(buf, &tunables->pl))
+ return -EINVAL;
+
+ return count;
+}
+
static struct governor_attr rate_limit_us = __ATTR_RW(rate_limit_us);
static struct governor_attr hispeed_freq = __ATTR_RW(hispeed_freq);
+static struct governor_attr pl = __ATTR_RW(pl);
static struct attribute *sugov_attributes[] = {
&rate_limit_us.attr,
&hispeed_freq.attr,
+ &pl.attr,
NULL
};
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index eb6c9f1..8d2b4d8 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1484,6 +1484,11 @@
end:
release_all_trace_kprobes();
+ /*
+ * Wait for the optimizer work to finish. Otherwise it might fiddle
+ * with probes in already freed __init text.
+ */
+ wait_for_kprobe_optimizer();
if (warn)
pr_cont("NG: Some tests are failed. Please check them.\n");
else
diff --git a/net/core/filter.c b/net/core/filter.c
index b391209..2cb4f0f 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -26,6 +26,7 @@
#include <linux/mm.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
+#include <linux/sock_diag.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
@@ -78,6 +79,10 @@
if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC))
return -ENOMEM;
+ err = BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb);
+ if (err)
+ return err;
+
err = security_sock_rcv_skb(sk, skb);
if (err)
return err;
@@ -85,8 +90,13 @@
rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter) {
- unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
+ struct sock *save_sk = skb->sk;
+ unsigned int pkt_len;
+
+ skb->sk = sk;
+ pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
+ skb->sk = save_sk;
}
rcu_read_unlock();
@@ -2530,6 +2540,36 @@
.arg5_type = ARG_CONST_STACK_SIZE,
};
+BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb)
+{
+ return skb->sk ? sock_gen_cookie(skb->sk) : 0;
+}
+
+static const struct bpf_func_proto bpf_get_socket_cookie_proto = {
+ .func = bpf_get_socket_cookie,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+};
+
+BPF_CALL_1(bpf_get_socket_uid, struct sk_buff *, skb)
+{
+ struct sock *sk = sk_to_full_sk(skb->sk);
+ kuid_t kuid;
+
+ if (!sk || !sk_fullsock(sk))
+ return overflowuid;
+ kuid = sock_net_uid(sock_net(sk), sk);
+ return from_kuid_munged(sock_net(sk)->user_ns, kuid);
+}
+
+static const struct bpf_func_proto bpf_get_socket_uid_proto = {
+ .func = bpf_get_socket_uid,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+};
+
static const struct bpf_func_proto *
sk_filter_func_proto(enum bpf_func_id func_id)
{
@@ -2551,6 +2591,10 @@
case BPF_FUNC_trace_printk:
if (capable(CAP_SYS_ADMIN))
return bpf_get_trace_printk_proto();
+ case BPF_FUNC_get_socket_cookie:
+ return &bpf_get_socket_cookie_proto;
+ case BPF_FUNC_get_socket_uid:
+ return &bpf_get_socket_uid_proto;
default:
return NULL;
}
@@ -2628,6 +2672,17 @@
}
}
+static const struct bpf_func_proto *
+cg_skb_func_proto(enum bpf_func_id func_id)
+{
+ switch (func_id) {
+ case BPF_FUNC_skb_load_bytes:
+ return &bpf_skb_load_bytes_proto;
+ default:
+ return sk_filter_func_proto(func_id);
+ }
+}
+
static bool __is_valid_access(int off, int size, enum bpf_access_type type)
{
if (off < 0 || off >= sizeof(struct __sk_buff))
@@ -2990,6 +3045,12 @@
.convert_ctx_access = xdp_convert_ctx_access,
};
+static const struct bpf_verifier_ops cg_skb_ops = {
+ .get_func_proto = cg_skb_func_proto,
+ .is_valid_access = sk_filter_is_valid_access,
+ .convert_ctx_access = sk_filter_convert_ctx_access,
+};
+
static struct bpf_prog_type_list sk_filter_type __read_mostly = {
.ops = &sk_filter_ops,
.type = BPF_PROG_TYPE_SOCKET_FILTER,
@@ -3010,12 +3071,18 @@
.type = BPF_PROG_TYPE_XDP,
};
+static struct bpf_prog_type_list cg_skb_type __read_mostly = {
+ .ops = &cg_skb_ops,
+ .type = BPF_PROG_TYPE_CGROUP_SKB,
+};
+
static int __init register_sk_filter_ops(void)
{
bpf_register_prog_type(&sk_filter_type);
bpf_register_prog_type(&sched_cls_type);
bpf_register_prog_type(&sched_act_type);
bpf_register_prog_type(&xdp_type);
+ bpf_register_prog_type(&cg_skb_type);
return 0;
}
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index 6b10573..acd2a6c 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -19,7 +19,7 @@
static DEFINE_MUTEX(sock_diag_table_mutex);
static struct workqueue_struct *broadcast_wq;
-static u64 sock_gen_cookie(struct sock *sk)
+u64 sock_gen_cookie(struct sock *sk)
{
while (1) {
u64 res = atomic64_read(&sk->sk_cookie);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 0bd3efe..2c18bcf 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -74,6 +74,7 @@
#include <net/checksum.h>
#include <net/inetpeer.h>
#include <net/lwtunnel.h>
+#include <linux/bpf-cgroup.h>
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
@@ -287,6 +288,13 @@
static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
unsigned int mtu;
+ int ret;
+
+ ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
+ if (ret) {
+ kfree_skb(skb);
+ return ret;
+ }
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
/* Policy lookup after SNAT yielded a new policy */
@@ -305,6 +313,20 @@
return ip_finish_output2(net, sk, skb);
}
+static int ip_mc_finish_output(struct net *net, struct sock *sk,
+ struct sk_buff *skb)
+{
+ int ret;
+
+ ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
+ if (ret) {
+ kfree_skb(skb);
+ return ret;
+ }
+
+ return dev_loopback_xmit(net, sk, skb);
+}
+
int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct rtable *rt = skb_rtable(skb);
@@ -342,7 +364,7 @@
if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, newskb, NULL, newskb->dev,
- dev_loopback_xmit);
+ ip_mc_finish_output);
}
/* Multicasts with ttl 0 must not go beyond the host */
@@ -358,7 +380,7 @@
if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, newskb, NULL, newskb->dev,
- dev_loopback_xmit);
+ ip_mc_finish_output);
}
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index e27b8fd..a215802 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -39,6 +39,7 @@
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/bpf-cgroup.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
@@ -131,6 +132,14 @@
static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
+ int ret;
+
+ ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
+ if (ret) {
+ kfree_skb(skb);
+ return ret;
+ }
+
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)) ||
(IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 48d0dc89b..e735f78 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -1168,11 +1168,10 @@
sipx->sipx_network = ipxif->if_netnum;
memcpy(sipx->sipx_node, ipxif->if_node,
sizeof(sipx->sipx_node));
- rc = -EFAULT;
- if (copy_to_user(arg, &ifr, sizeof(ifr)))
- break;
- ipxitf_put(ipxif);
rc = 0;
+ if (copy_to_user(arg, &ifr, sizeof(ifr)))
+ rc = -EFAULT;
+ ipxitf_put(ipxif);
break;
}
case SIOCAIPXITFCRT:
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 72c5867..b2cdced 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -22,6 +22,7 @@
hostprogs-y += map_perf_test
hostprogs-y += test_overhead
hostprogs-y += test_cgrp2_array_pin
+hostprogs-y += test_cgrp2_attach
hostprogs-y += xdp1
hostprogs-y += xdp2
hostprogs-y += test_current_task_under_cgroup
@@ -50,6 +51,7 @@
map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o
test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o
test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o
+test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o
xdp1-objs := bpf_load.o libbpf.o xdp1_user.o
# reuse xdp1 source intentionally
xdp2-objs := bpf_load.o libbpf.o xdp1_user.o
diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c
index 9969e35..9ce707b 100644
--- a/samples/bpf/libbpf.c
+++ b/samples/bpf/libbpf.c
@@ -104,6 +104,27 @@
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
+int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr = {
+ .target_fd = target_fd,
+ .attach_bpf_fd = prog_fd,
+ .attach_type = type,
+ };
+
+ return syscall(__NR_bpf, BPF_PROG_ATTACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr = {
+ .target_fd = target_fd,
+ .attach_type = type,
+ };
+
+ return syscall(__NR_bpf, BPF_PROG_DETACH, &attr, sizeof(attr));
+}
+
int bpf_obj_pin(int fd, const char *pathname)
{
union bpf_attr attr = {
diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h
index ac6edb6..d0a799a 100644
--- a/samples/bpf/libbpf.h
+++ b/samples/bpf/libbpf.h
@@ -15,6 +15,9 @@
const struct bpf_insn *insns, int insn_len,
const char *license, int kern_version);
+int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type);
+int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
+
int bpf_obj_pin(int fd, const char *pathname);
int bpf_obj_get(const char *pathname);
diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c
new file mode 100644
index 0000000..63ef208
--- /dev/null
+++ b/samples/bpf/test_cgrp2_attach.c
@@ -0,0 +1,147 @@
+/* eBPF example program:
+ *
+ * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
+ *
+ * - Loads eBPF program
+ *
+ * The eBPF program accesses the map passed in to store two pieces of
+ * information. The number of invocations of the program, which maps
+ * to the number of packets received, is stored to key 0. Key 1 is
+ * incremented on each iteration by the number of bytes stored in
+ * the skb.
+ *
+ * - Detaches any eBPF program previously attached to the cgroup
+ *
+ * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
+ *
+ * - Every second, reads map[0] and map[1] to see how many bytes and
+ * packets were seen on any socket of tasks in the given cgroup.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/bpf.h>
+
+#include "libbpf.h"
+
+enum {
+ MAP_KEY_PACKETS,
+ MAP_KEY_BYTES,
+};
+
+static int prog_load(int map_fd, int verdict)
+{
+ struct bpf_insn prog[] = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* save r6 so it's not clobbered by BPF_CALL */
+
+ /* Count packets */
+ BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_PACKETS), /* r0 = 0 */
+ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
+ BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* load map fd to r1 */
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+ BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
+ BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+
+ /* Count bytes */
+ BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */
+ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
+ BPF_LD_MAP_FD(BPF_REG_1, map_fd),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+ BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */
+ BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+
+ BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
+ BPF_EXIT_INSN(),
+ };
+
+ return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB,
+ prog, sizeof(prog), "GPL", 0);
+}
+
+static int usage(const char *argv0)
+{
+ printf("Usage: %s <cg-path> <egress|ingress> [drop]\n", argv0);
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char **argv)
+{
+ int cg_fd, map_fd, prog_fd, key, ret;
+ long long pkt_cnt, byte_cnt;
+ enum bpf_attach_type type;
+ int verdict = 1;
+
+ if (argc < 3)
+ return usage(argv[0]);
+
+ if (strcmp(argv[2], "ingress") == 0)
+ type = BPF_CGROUP_INET_INGRESS;
+ else if (strcmp(argv[2], "egress") == 0)
+ type = BPF_CGROUP_INET_EGRESS;
+ else
+ return usage(argv[0]);
+
+ if (argc > 3 && strcmp(argv[3], "drop") == 0)
+ verdict = 0;
+
+ cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
+ if (cg_fd < 0) {
+ printf("Failed to open cgroup path: '%s'\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY,
+ sizeof(key), sizeof(byte_cnt),
+ 256, 0);
+ if (map_fd < 0) {
+ printf("Failed to create map: '%s'\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ prog_fd = prog_load(map_fd, verdict);
+ printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf);
+
+ if (prog_fd < 0) {
+ printf("Failed to load prog: '%s'\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ ret = bpf_prog_detach(cg_fd, type);
+ printf("bpf_prog_detach() returned '%s' (%d)\n", strerror(errno), errno);
+
+ ret = bpf_prog_attach(prog_fd, cg_fd, type);
+ if (ret < 0) {
+ printf("Failed to attach prog to cgroup: '%s'\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ while (1) {
+ key = MAP_KEY_PACKETS;
+ assert(bpf_lookup_elem(map_fd, &key, &pkt_cnt) == 0);
+
+ key = MAP_KEY_BYTES;
+ assert(bpf_lookup_elem(map_fd, &key, &byte_cnt) == 0);
+
+ printf("cgroup received %lld packets, %lld bytes\n",
+ pkt_cnt, byte_cnt);
+ sleep(1);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 389325a..0974598 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -204,10 +204,11 @@
cause = "missing-hash";
status = INTEGRITY_NOLABEL;
- if (opened & FILE_CREATED) {
+ if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
+ if ((iint->flags & IMA_NEW_FILE) &&
+ !(iint->flags & IMA_DIGSIG_REQUIRED))
status = INTEGRITY_PASS;
- }
goto out;
}
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 0430658..0f41257 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -106,7 +106,11 @@
/* disable ringbuffer DMAs */
snd_hdac_chip_writeb(bus, RIRBCTL, 0);
snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ spin_unlock_irq(&bus->reg_lock);
+
hdac_wait_for_cmd_dmas(bus);
+
+ spin_lock_irq(&bus->reg_lock);
/* disable unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
spin_unlock_irq(&bus->reg_lock);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 8c0f3b8..e78b5f0 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -498,7 +498,7 @@
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(cs4271->gpio_nreset)) {
- gpio_set_value(cs4271->gpio_nreset, 0);
+ gpio_direction_output(cs4271->gpio_nreset, 0);
mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
mdelay(1);
diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c
index 0b07393..4278e36 100644
--- a/sound/soc/codecs/wcd9330.c
+++ b/sound/soc/codecs/wcd9330.c
@@ -1536,6 +1536,13 @@
tomtom_mad_input = ucontrol->value.integer.value[0];
micb_4_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias;
+ if (tomtom_mad_input >= ARRAY_SIZE(tomtom_conn_mad_text)) {
+ dev_err(codec->dev,
+ "%s: tomtom_mad_input = %d out of bounds\n",
+ __func__, tomtom_mad_input);
+ return -EINVAL;
+ }
+
pr_debug("%s: tomtom_mad_input = %s\n", __func__,
tomtom_conn_mad_text[tomtom_mad_input]);
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index d4db55f..36382ba 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
-snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \
- msm-compress-q6-v2.o msm-compr-q6-v2.o \
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o \
+ msm-pcm-routing-v2.o msm-compress-q6-v2.o \
msm-pcm-afe-v2.o msm-pcm-voip-v2.o \
msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \
msm-lsm-client.o msm-pcm-host-voice-v2.o \
diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
index 5d4a0ba..820aa1b 100644
--- a/sound/soc/msm/qdsp6v2/audio_cal_utils.c
+++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
@@ -646,7 +646,9 @@
return cal_block;
err:
kfree(cal_block->cal_info);
+ cal_block->cal_info = NULL;
kfree(cal_block->client_info);
+ cal_block->client_info = NULL;
kfree(cal_block);
cal_block = NULL;
return cal_block;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
deleted file mode 100644
index 449325c..0000000
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ /dev/null
@@ -1,1714 +0,0 @@
-/* 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/time.h>
-#include <linux/wait.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <sound/q6asm-v2.h>
-#include <sound/pcm_params.h>
-#include <asm/dma.h>
-#include <linux/dma-mapping.h>
-#include <linux/msm_audio_ion.h>
-
-#include <sound/timer.h>
-
-#include "msm-compr-q6-v2.h"
-#include "msm-pcm-routing-v2.h"
-#include <sound/tlv.h>
-
-#define COMPRE_CAPTURE_NUM_PERIODS 16
-/* Allocate the worst case frame size for compressed audio */
-#define COMPRE_CAPTURE_HEADER_SIZE (sizeof(struct snd_compr_audio_info))
-/* Changing period size to 4032. 4032 will make sure COMPRE_CAPTURE_PERIOD_SIZE
- * is 4096 with meta data size of 64 and MAX_NUM_FRAMES_PER_BUFFER 1
- */
-#define COMPRE_CAPTURE_MAX_FRAME_SIZE (4032)
-#define COMPRE_CAPTURE_PERIOD_SIZE ((COMPRE_CAPTURE_MAX_FRAME_SIZE + \
- COMPRE_CAPTURE_HEADER_SIZE) * \
- MAX_NUM_FRAMES_PER_BUFFER)
-#define COMPRE_OUTPUT_METADATA_SIZE (sizeof(struct output_meta_data_st))
-#define COMPRESSED_LR_VOL_MAX_STEPS 0x20002000
-
-#define MAX_AC3_PARAM_SIZE (18*2*sizeof(int))
-#define AMR_WB_BAND_MODE 8
-#define AMR_WB_DTX_MODE 0
-
-
-const DECLARE_TLV_DB_LINEAR(compr_rx_vol_gain, 0,
- COMPRESSED_LR_VOL_MAX_STEPS);
-
-static struct audio_locks the_locks;
-
-static struct snd_pcm_hardware msm_compr_hardware_capture = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 8,
- .buffer_bytes_max =
- COMPRE_CAPTURE_PERIOD_SIZE * COMPRE_CAPTURE_NUM_PERIODS,
- .period_bytes_min = COMPRE_CAPTURE_PERIOD_SIZE,
- .period_bytes_max = COMPRE_CAPTURE_PERIOD_SIZE,
- .periods_min = COMPRE_CAPTURE_NUM_PERIODS,
- .periods_max = COMPRE_CAPTURE_NUM_PERIODS,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware msm_compr_hardware_playback = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 8,
- .buffer_bytes_max = 1024 * 1024,
- .period_bytes_min = 128 * 1024,
- .period_bytes_max = 256 * 1024,
- .periods_min = 4,
- .periods_max = 8,
- .fifo_size = 0,
-};
-
-/* Conventional and unconventional sample rate supported */
-static unsigned int supported_sample_rates[] = {
- 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
-};
-
-/* Add supported codecs for compress capture path */
-static uint32_t supported_compr_capture_codecs[] = {
- SND_AUDIOCODEC_AMRWB
-};
-
-static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
- .count = ARRAY_SIZE(supported_sample_rates),
- .list = supported_sample_rates,
- .mask = 0,
-};
-
-static bool msm_compr_capture_codecs(uint32_t req_codec)
-{
- int i;
-
- pr_debug("%s req_codec:%d\n", __func__, req_codec);
- if (req_codec == 0)
- return false;
- for (i = 0; i < ARRAY_SIZE(supported_compr_capture_codecs); i++) {
- if (req_codec == supported_compr_capture_codecs[i])
- return true;
- }
- return false;
-}
-
-static void compr_event_handler(uint32_t opcode,
- uint32_t token, uint32_t *payload, void *priv)
-{
- struct compr_audio *compr = priv;
- struct msm_audio *prtd = &compr->prtd;
- struct snd_pcm_substream *substream = prtd->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_aio_write_param param;
- struct audio_aio_read_param read_param;
- struct audio_buffer *buf = NULL;
- phys_addr_t temp;
- struct output_meta_data_st output_meta_data;
- uint32_t *ptrmem = (uint32_t *)payload;
- int i = 0;
- int time_stamp_flag = 0;
- int buffer_length = 0;
- int stop_playback = 0;
-
- pr_debug("%s opcode =%08x\n", __func__, opcode);
- switch (opcode) {
- case ASM_DATA_EVENT_WRITE_DONE_V2: {
- uint32_t *ptrmem = (uint32_t *)¶m;
-
- pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
- pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
- prtd->pcm_irq_pos += prtd->pcm_count;
- if (atomic_read(&prtd->start))
- snd_pcm_period_elapsed(substream);
- else
- if (substream->timer_running)
- snd_timer_interrupt(substream->timer, 1);
- atomic_inc(&prtd->out_count);
- wake_up(&the_locks.write_wait);
- if (!atomic_read(&prtd->start)) {
- atomic_set(&prtd->pending_buffer, 1);
- break;
- }
- atomic_set(&prtd->pending_buffer, 0);
-
- /*
- * check for underrun
- */
- snd_pcm_stream_lock_irq(substream);
- if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
- runtime->render_flag |= SNDRV_RENDER_STOPPED;
- stop_playback = 1;
- }
- snd_pcm_stream_unlock_irq(substream);
-
- if (stop_playback) {
- pr_err("underrun! render stopped\n");
- break;
- }
-
- buf = prtd->audio_client->port[IN].buf;
- pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
- __func__, prtd->pcm_count, prtd->out_head);
- temp = buf[0].phys + (prtd->out_head * prtd->pcm_count);
- pr_debug("%s:writing buffer[%d] from 0x%pK\n",
- __func__, prtd->out_head, &temp);
-
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- time_stamp_flag = SET_TIMESTAMP;
- else
- time_stamp_flag = NO_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
- prtd->out_head * prtd->pcm_count),
- COMPRE_OUTPUT_METADATA_SIZE);
-
- buffer_length = output_meta_data.frame_size;
- pr_debug("meta_data_length: %d, frame_length: %d\n",
- output_meta_data.meta_data_length,
- output_meta_data.frame_size);
- pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
- output_meta_data.timestamp_msw,
- output_meta_data.timestamp_lsw);
- if (buffer_length == 0) {
- pr_debug("Received a zero length buffer-break out");
- break;
- }
- param.paddr = temp + output_meta_data.meta_data_length;
- param.len = buffer_length;
- param.msw_ts = output_meta_data.timestamp_msw;
- param.lsw_ts = output_meta_data.timestamp_lsw;
- param.flags = time_stamp_flag;
- param.uid = prtd->session_id;
- for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
- i++, ++ptrmem)
- pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
- if (q6asm_async_write(prtd->audio_client,
- ¶m) < 0)
- pr_err("%s:q6asm_async_write failed\n",
- __func__);
- else
- prtd->out_head =
- (prtd->out_head + 1) & (runtime->periods - 1);
- break;
- }
- case ASM_DATA_EVENT_RENDERED_EOS:
- pr_debug("ASM_DATA_CMDRSP_EOS\n");
- if (atomic_read(&prtd->eos)) {
- pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
- prtd->cmd_ack = 1;
- wake_up(&the_locks.eos_wait);
- atomic_set(&prtd->eos, 0);
- }
- break;
- case ASM_DATA_EVENT_READ_DONE_V2: {
- pr_debug("ASM_DATA_EVENT_READ_DONE\n");
- pr_debug("buf = %pK, data = 0x%X, *data = %pK,\n"
- "prtd->pcm_irq_pos = %d\n",
- prtd->audio_client->port[OUT].buf,
- *(uint32_t *)prtd->audio_client->port[OUT].buf->data,
- prtd->audio_client->port[OUT].buf->data,
- prtd->pcm_irq_pos);
-
- memcpy(prtd->audio_client->port[OUT].buf->data +
- prtd->pcm_irq_pos, (ptrmem + READDONE_IDX_SIZE),
- COMPRE_CAPTURE_HEADER_SIZE);
- pr_debug("buf = %pK, updated data = 0x%X, *data = %pK\n",
- prtd->audio_client->port[OUT].buf,
- *(uint32_t *)(prtd->audio_client->port[OUT].buf->data +
- prtd->pcm_irq_pos),
- prtd->audio_client->port[OUT].buf->data);
- if (!atomic_read(&prtd->start))
- break;
- pr_debug("frame size=%d, buffer = 0x%X\n",
- ptrmem[READDONE_IDX_SIZE],
- ptrmem[READDONE_IDX_BUFADD_LSW]);
- if (ptrmem[READDONE_IDX_SIZE] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
- pr_err("Frame length exceeded the max length");
- break;
- }
- buf = prtd->audio_client->port[OUT].buf;
-
- pr_debug("pcm_irq_pos=%d, buf[0].phys = 0x%pK\n",
- prtd->pcm_irq_pos, &buf[0].phys);
- read_param.len = prtd->pcm_count - COMPRE_CAPTURE_HEADER_SIZE;
- read_param.paddr = buf[0].phys +
- prtd->pcm_irq_pos + COMPRE_CAPTURE_HEADER_SIZE;
- prtd->pcm_irq_pos += prtd->pcm_count;
-
- if (atomic_read(&prtd->start))
- snd_pcm_period_elapsed(substream);
-
- q6asm_async_read(prtd->audio_client, &read_param);
- break;
- }
- case APR_BASIC_RSP_RESULT: {
- switch (payload[0]) {
- case ASM_SESSION_CMD_RUN_V2: {
- if (substream->stream
- != SNDRV_PCM_STREAM_PLAYBACK) {
- atomic_set(&prtd->start, 1);
- break;
- }
- if (!atomic_read(&prtd->pending_buffer))
- break;
- pr_debug("%s: writing %d bytes of buffer[%d] to dsp\n",
- __func__, prtd->pcm_count, prtd->out_head);
- buf = prtd->audio_client->port[IN].buf;
- pr_debug("%s: writing buffer[%d] from 0x%pK head %d count %d\n",
- __func__, prtd->out_head, &buf[0].phys,
- prtd->pcm_count, prtd->out_head);
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- time_stamp_flag = SET_TIMESTAMP;
- else
- time_stamp_flag = NO_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
- prtd->out_head * prtd->pcm_count),
- COMPRE_OUTPUT_METADATA_SIZE);
- buffer_length = output_meta_data.frame_size;
- pr_debug("meta_data_length: %d, frame_length: %d\n",
- output_meta_data.meta_data_length,
- output_meta_data.frame_size);
- pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
- output_meta_data.timestamp_msw,
- output_meta_data.timestamp_lsw);
- param.paddr = buf[prtd->out_head].phys
- + output_meta_data.meta_data_length;
- param.len = buffer_length;
- param.msw_ts = output_meta_data.timestamp_msw;
- param.lsw_ts = output_meta_data.timestamp_lsw;
- param.flags = time_stamp_flag;
- param.uid = prtd->session_id;
- param.metadata_len = COMPRE_OUTPUT_METADATA_SIZE;
- if (q6asm_async_write(prtd->audio_client,
- ¶m) < 0)
- pr_err("%s:q6asm_async_write failed\n",
- __func__);
- else
- prtd->out_head =
- (prtd->out_head + 1)
- & (runtime->periods - 1);
- atomic_set(&prtd->pending_buffer, 0);
- }
- break;
- case ASM_STREAM_CMD_FLUSH:
- pr_debug("ASM_STREAM_CMD_FLUSH\n");
- prtd->cmd_ack = 1;
- wake_up(&the_locks.flush_wait);
- break;
- default:
- break;
- }
- break;
- }
- default:
- pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
- break;
- }
-}
-
-static int msm_compr_send_ddp_cfg(struct audio_client *ac,
- struct snd_dec_ddp *ddp)
-{
- int i, rc;
-
- pr_debug("%s\n", __func__);
-
- if (ddp->params_length / 2 > SND_DEC_DDP_MAX_PARAMS) {
- pr_err("%s: Invalid number of params %u, max allowed %u\n",
- __func__, ddp->params_length / 2,
- SND_DEC_DDP_MAX_PARAMS);
- return -EINVAL;
- }
-
- for (i = 0; i < ddp->params_length/2; i++) {
- rc = q6asm_ds1_set_endp_params(ac, ddp->params_id[i],
- ddp->params_value[i]);
- if (rc) {
- pr_err("sending params_id: %d failed\n",
- ddp->params_id[i]);
- return rc;
- }
- }
- return 0;
-}
-
-static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct msm_audio *prtd = &compr->prtd;
- struct snd_pcm_hw_params *params;
- struct asm_aac_cfg aac_cfg;
- uint16_t bits_per_sample = 16;
- int ret;
-
- struct asm_softpause_params softpause = {
- .enable = SOFT_PAUSE_ENABLE,
- .period = SOFT_PAUSE_PERIOD,
- .step = SOFT_PAUSE_STEP,
- .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
- };
- struct asm_softvolume_params softvol = {
- .period = SOFT_VOLUME_PERIOD,
- .step = SOFT_VOLUME_STEP,
- .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
- };
-
- pr_debug("%s\n", __func__);
-
- params = &soc_prtd->dpcm[substream->stream].hw_params;
- if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
- bits_per_sample = 24;
-
- ret = q6asm_open_write_v2(prtd->audio_client,
- compr->codec, bits_per_sample);
- if (ret < 0) {
- pr_err("%s: Session out open failed\n",
- __func__);
- return -ENOMEM;
- }
- msm_pcm_routing_reg_phy_stream(
- soc_prtd->dai_link->id,
- prtd->audio_client->perf_mode,
- prtd->session_id,
- substream->stream);
- /*
- * the number of channels are required to call volume api
- * accoridngly. So, get channels from hw params
- */
- if ((params_channels(params) > 0) &&
- (params_periods(params) <= runtime->hw.channels_max))
- prtd->channel_mode = params_channels(params);
-
- ret = q6asm_set_softpause(prtd->audio_client, &softpause);
- if (ret < 0)
- pr_err("%s: Send SoftPause Param failed ret=%d\n",
- __func__, ret);
- ret = q6asm_set_softvolume(prtd->audio_client, &softvol);
- if (ret < 0)
- pr_err("%s: Send SoftVolume Param failed ret=%d\n",
- __func__, ret);
-
- ret = q6asm_set_io_mode(prtd->audio_client,
- (COMPRESSED_IO | ASYNC_IO_MODE));
- if (ret < 0) {
- pr_err("%s: Set IO mode failed\n", __func__);
- return -ENOMEM;
- }
-
- prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
- prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
- prtd->pcm_irq_pos = 0;
- /* rate and channels are sent to audio driver */
- prtd->samp_rate = runtime->rate;
- prtd->channel_mode = runtime->channels;
- prtd->out_head = 0;
- atomic_set(&prtd->out_count, runtime->periods);
-
- if (prtd->enabled)
- return 0;
-
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_MP3:
- /* No media format block for mp3 */
- break;
- case SND_AUDIOCODEC_AAC:
- pr_debug("%s: SND_AUDIOCODEC_AAC\n", __func__);
- memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
- aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
- aac_cfg.format = 0x03;
- aac_cfg.ch_cfg = runtime->channels;
- aac_cfg.sample_rate = runtime->rate;
- ret = q6asm_media_format_block_aac(prtd->audio_client,
- &aac_cfg);
- if (ret < 0)
- pr_err("%s: CMD Format block failed\n", __func__);
- break;
- case SND_AUDIOCODEC_AC3: {
- struct snd_dec_ddp *ddp =
- &compr->info.codec_param.codec.options.ddp;
- pr_debug("%s: SND_AUDIOCODEC_AC3\n", __func__);
- ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp);
- if (ret < 0)
- pr_err("%s: DDP CMD CFG failed\n", __func__);
- break;
- }
- case SND_AUDIOCODEC_EAC3: {
- struct snd_dec_ddp *ddp =
- &compr->info.codec_param.codec.options.ddp;
- pr_debug("%s: SND_AUDIOCODEC_EAC3\n", __func__);
- ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp);
- if (ret < 0)
- pr_err("%s: DDP CMD CFG failed\n", __func__);
- break;
- }
- default:
- return -EINVAL;
- }
-
- prtd->enabled = 1;
- prtd->cmd_ack = 0;
- prtd->cmd_interrupt = 0;
-
- return 0;
-}
-
-static int msm_compr_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- struct audio_buffer *buf = prtd->audio_client->port[OUT].buf;
- struct snd_codec *codec = &compr->info.codec_param.codec;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct audio_aio_read_param read_param;
- uint16_t bits_per_sample = 16;
- int ret = 0;
- int i;
-
- prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
- prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
- prtd->pcm_irq_pos = 0;
-
- if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
- bits_per_sample = 24;
-
- if (!msm_compr_capture_codecs(
- compr->info.codec_param.codec.id)) {
- /*
- * request codec invalid or not supported,
- * use default compress format
- */
- compr->info.codec_param.codec.id =
- SND_AUDIOCODEC_AMRWB;
- }
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_AMRWB:
- pr_debug("q6asm_open_read(FORMAT_AMRWB)\n");
- ret = q6asm_open_read(prtd->audio_client,
- FORMAT_AMRWB);
- if (ret < 0) {
- pr_err("%s: compressed Session out open failed\n",
- __func__);
- return -ENOMEM;
- }
- pr_debug("msm_pcm_routing_reg_phy_stream\n");
- msm_pcm_routing_reg_phy_stream(
- soc_prtd->dai_link->id,
- prtd->audio_client->perf_mode,
- prtd->session_id, substream->stream);
- break;
- default:
- pr_debug("q6asm_open_read_compressed(COMPRESSED_META_DATA_MODE)\n");
- /*
- * ret = q6asm_open_read_compressed(prtd->audio_client,
- * MAX_NUM_FRAMES_PER_BUFFER,
- * COMPRESSED_META_DATA_MODE);
- */
- ret = -EINVAL;
- break;
- }
-
- if (ret < 0) {
- pr_err("%s: compressed Session out open failed\n",
- __func__);
- return -ENOMEM;
- }
-
- ret = q6asm_set_io_mode(prtd->audio_client,
- (COMPRESSED_IO | ASYNC_IO_MODE));
- if (ret < 0) {
- pr_err("%s: Set IO mode failed\n", __func__);
- return -ENOMEM;
- }
-
- if (!msm_compr_capture_codecs(codec->id)) {
- /*
- * request codec invalid or not supported,
- * use default compress format
- */
- codec->id = SND_AUDIOCODEC_AMRWB;
- }
- /* rate and channels are sent to audio driver */
- prtd->samp_rate = runtime->rate;
- prtd->channel_mode = runtime->channels;
-
- if (prtd->enabled)
- return ret;
- read_param.len = prtd->pcm_count;
-
- switch (codec->id) {
- case SND_AUDIOCODEC_AMRWB:
- pr_debug("SND_AUDIOCODEC_AMRWB\n");
- ret = q6asm_enc_cfg_blk_amrwb(prtd->audio_client,
- MAX_NUM_FRAMES_PER_BUFFER,
- /*
- * use fixed band mode and dtx mode
- * band mode - 23.85 kbps
- */
- AMR_WB_BAND_MODE,
- /* dtx mode - disable */
- AMR_WB_DTX_MODE);
- if (ret < 0)
- pr_err("%s: CMD Format block failed: %d\n",
- __func__, ret);
- break;
- default:
- pr_debug("No config for codec %d\n", codec->id);
- }
- pr_debug("%s: Samp_rate = %d, Channel = %d, pcm_size = %d,\n"
- "pcm_count = %d, periods = %d\n",
- __func__, prtd->samp_rate, prtd->channel_mode,
- prtd->pcm_size, prtd->pcm_count, runtime->periods);
-
- for (i = 0; i < runtime->periods; i++) {
- read_param.uid = i;
- switch (codec->id) {
- case SND_AUDIOCODEC_AMRWB:
- read_param.len = prtd->pcm_count
- - COMPRE_CAPTURE_HEADER_SIZE;
- read_param.paddr = buf[i].phys
- + COMPRE_CAPTURE_HEADER_SIZE;
- pr_debug("Push buffer [%d] to DSP, paddr: %pK, vaddr: %pK\n",
- i, &read_param.paddr,
- buf[i].data);
- q6asm_async_read(prtd->audio_client, &read_param);
- break;
- default:
- read_param.paddr = buf[i].phys;
- /* q6asm_async_read_compressed(prtd->audio_client,
- * &read_param);
- */
- pr_debug("%s: To add support for read compressed\n",
- __func__);
- ret = -EINVAL;
- break;
- }
- }
- prtd->periods = runtime->periods;
-
- prtd->enabled = 1;
-
- return ret;
-}
-
-static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- int ret = 0;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
-
- pr_debug("%s\n", __func__);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- prtd->pcm_irq_pos = 0;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (!msm_compr_capture_codecs(
- compr->info.codec_param.codec.id)) {
- /*
- * request codec invalid or not supported,
- * use default compress format
- */
- compr->info.codec_param.codec.id =
- SND_AUDIOCODEC_AMRWB;
- }
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_AMRWB:
- break;
- default:
- msm_pcm_routing_reg_psthr_stream(
- soc_prtd->dai_link->id,
- prtd->session_id, substream->stream);
- break;
- }
- }
- atomic_set(&prtd->pending_buffer, 1);
- /* fallthrough */
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- pr_debug("%s: Trigger start\n", __func__);
- q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
- atomic_set(&prtd->start, 1);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_AMRWB:
- break;
- default:
- msm_pcm_routing_reg_psthr_stream(
- soc_prtd->dai_link->id,
- prtd->session_id, substream->stream);
- break;
- }
- }
- atomic_set(&prtd->start, 0);
- runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
- q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
- atomic_set(&prtd->start, 0);
- runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static void populate_codec_list(struct compr_audio *compr,
- struct snd_pcm_runtime *runtime)
-{
- pr_debug("%s\n", __func__);
- /* MP3 Block */
- compr->info.compr_cap.num_codecs = 5;
- compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
- compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
- compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
- compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
- compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
- compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
- compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
- compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3;
- compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_AMRWB;
- /* Add new codecs here */
-}
-
-static int msm_compr_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr;
- struct msm_audio *prtd;
- int ret = 0;
-
- pr_debug("%s\n", __func__);
- compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
- if (compr == NULL) {
- pr_err("Failed to allocate memory for msm_audio\n");
- return -ENOMEM;
- }
- prtd = &compr->prtd;
- prtd->substream = substream;
- runtime->render_flag = SNDRV_DMA_MODE;
- prtd->audio_client = q6asm_audio_client_alloc(
- (app_cb)compr_event_handler, compr);
- if (!prtd->audio_client) {
- pr_info("%s: Could not allocate memory\n", __func__);
- kfree(prtd);
- return -ENOMEM;
- }
-
- prtd->audio_client->perf_mode = false;
- pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
-
- prtd->session_id = prtd->audio_client->session;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw = msm_compr_hardware_playback;
- prtd->cmd_ack = 1;
- } else {
- runtime->hw = msm_compr_hardware_capture;
- }
-
-
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_sample_rates);
- if (ret < 0)
- pr_info("snd_pcm_hw_constraint_list failed\n");
- /* Ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- pr_info("snd_pcm_hw_constraint_integer failed\n");
-
- prtd->dsp_cnt = 0;
- atomic_set(&prtd->pending_buffer, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- compr->codec = FORMAT_MP3;
- populate_codec_list(compr, runtime);
- runtime->private_data = compr;
- atomic_set(&prtd->eos, 0);
- return 0;
-}
-
-static int compressed_set_volume(struct msm_audio *prtd, uint32_t volume)
-{
- int rc = 0;
- int avg_vol = 0;
- int lgain = (volume >> 16) & 0xFFFF;
- int rgain = volume & 0xFFFF;
-
- if (prtd && prtd->audio_client) {
- pr_debug("%s: channels %d volume 0x%x\n", __func__,
- prtd->channel_mode, volume);
- if ((prtd->channel_mode == 2) &&
- (lgain != rgain)) {
- pr_debug("%s: call q6asm_set_lrgain\n", __func__);
- rc = q6asm_set_lrgain(prtd->audio_client, lgain, rgain);
- } else {
- avg_vol = (lgain + rgain)/2;
- pr_debug("%s: call q6asm_set_volume\n", __func__);
- rc = q6asm_set_volume(prtd->audio_client, avg_vol);
- }
- if (rc < 0) {
- pr_err("%s: Send Volume command failed rc=%d\n",
- __func__, rc);
- }
- }
- return rc;
-}
-
-static int msm_compr_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- int dir = 0;
-
- pr_debug("%s\n", __func__);
-
- dir = IN;
- atomic_set(&prtd->pending_buffer, 0);
-
- prtd->pcm_irq_pos = 0;
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- q6asm_audio_client_buf_free_contiguous(dir,
- prtd->audio_client);
- msm_pcm_routing_dereg_phy_stream(
- soc_prtd->dai_link->id,
- SNDRV_PCM_STREAM_PLAYBACK);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return 0;
-}
-
-static int msm_compr_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- int dir = OUT;
-
- pr_debug("%s\n", __func__);
- atomic_set(&prtd->pending_buffer, 0);
- q6asm_cmd(prtd->audio_client, CMD_CLOSE);
- q6asm_audio_client_buf_free_contiguous(dir,
- prtd->audio_client);
- msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
- SNDRV_PCM_STREAM_CAPTURE);
- q6asm_audio_client_free(prtd->audio_client);
- kfree(prtd);
- return 0;
-}
-
-static int msm_compr_close(struct snd_pcm_substream *substream)
-{
- int ret = 0;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ret = msm_compr_playback_close(substream);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ret = msm_compr_capture_close(substream);
- return ret;
-}
-
-static int msm_compr_prepare(struct snd_pcm_substream *substream)
-{
- int ret = 0;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ret = msm_compr_playback_prepare(substream);
- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- ret = msm_compr_capture_prepare(substream);
- return ret;
-}
-
-static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
-{
-
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
-
- if (prtd->pcm_irq_pos >= prtd->pcm_size)
- prtd->pcm_irq_pos = 0;
-
- pr_debug("%s: pcm_irq_pos = %d, pcm_size = %d, sample_bits = %d,\n"
- "frame_bits = %d\n", __func__, prtd->pcm_irq_pos,
- prtd->pcm_size, runtime->sample_bits,
- runtime->frame_bits);
- return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
-}
-
-static int msm_compr_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct msm_audio *prtd = runtime->private_data;
- struct audio_client *ac = prtd->audio_client;
- struct audio_port_data *apd = ac->port;
- struct audio_buffer *ab;
- int dir = -1;
-
- prtd->mmap_flag = 1;
- runtime->render_flag = SNDRV_NON_DMA_MODE;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dir = IN;
- else
- dir = OUT;
- ab = &(apd[dir].buf[0]);
-
- return msm_audio_ion_mmap(ab, vma);
-}
-
-static int msm_compr_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
- struct audio_buffer *buf;
- int dir, ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dir = IN;
- else
- dir = OUT;
- /* Modifying kernel hardware params based on userspace config */
- if (params_periods(params) > 0 &&
- (params_periods(params) != runtime->hw.periods_max)) {
- runtime->hw.periods_max = params_periods(params);
- }
- if (params_period_bytes(params) > 0 &&
- (params_period_bytes(params) != runtime->hw.period_bytes_min)) {
- runtime->hw.period_bytes_min = params_period_bytes(params);
- }
- runtime->hw.buffer_bytes_max =
- runtime->hw.period_bytes_min * runtime->hw.periods_max;
- pr_debug("allocate %zd buffers each of size %d\n",
- runtime->hw.period_bytes_min,
- runtime->hw.periods_max);
- ret = q6asm_audio_client_buf_alloc_contiguous(dir,
- prtd->audio_client,
- runtime->hw.period_bytes_min,
- runtime->hw.periods_max);
- if (ret < 0) {
- pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
- ret);
- return -ENOMEM;
- }
- buf = prtd->audio_client->port[dir].buf;
-
- dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
- dma_buf->dev.dev = substream->pcm->card->dev;
- dma_buf->private_data = NULL;
- dma_buf->area = buf[0].data;
- dma_buf->addr = buf[0].phys;
- dma_buf->bytes = runtime->hw.buffer_bytes_max;
-
- pr_debug("%s: buf[%pK]dma_buf->area[%pK]dma_buf->addr[%pK]\n"
- "dma_buf->bytes[%zd]\n", __func__,
- (void *)buf, (void *)dma_buf->area,
- &dma_buf->addr, dma_buf->bytes);
- if (!dma_buf->area)
- return -ENOMEM;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- return 0;
-}
-
-static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- int rc = 0;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- uint64_t timestamp;
- uint64_t temp;
-
- switch (cmd) {
- case SNDRV_COMPRESS_TSTAMP: {
- struct snd_compr_tstamp *tstamp;
-
- pr_debug("SNDRV_COMPRESS_TSTAMP\n");
- tstamp = arg;
- memset(tstamp, 0x0, sizeof(*tstamp));
- rc = q6asm_get_session_time(prtd->audio_client, ×tamp);
- if (rc < 0) {
- pr_err("%s: Get Session Time return value =%lld\n",
- __func__, timestamp);
- return -EAGAIN;
- }
- temp = (timestamp * 2 * runtime->channels);
- temp = temp * (runtime->rate/1000);
- temp = div_u64(temp, 1000);
- tstamp->sampling_rate = runtime->rate;
- tstamp->timestamp = timestamp;
- pr_debug("%s: bytes_consumed:,timestamp = %lld,\n",
- __func__,
- tstamp->timestamp);
- return 0;
- }
- case SNDRV_COMPRESS_GET_CAPS: {
- struct snd_compr_caps *caps;
-
- caps = arg;
- memset(caps, 0, sizeof(*caps));
- pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
- memcpy(caps, &compr->info.compr_cap, sizeof(*caps));
- return 0;
- }
- case SNDRV_COMPRESS_SET_PARAMS:
- pr_debug("SNDRV_COMPRESS_SET_PARAMS:\n");
- memcpy(&compr->info.codec_param, (void *) arg,
- sizeof(struct snd_compr_params));
- switch (compr->info.codec_param.codec.id) {
- case SND_AUDIOCODEC_MP3:
- /* For MP3 we dont need any other parameter */
- pr_debug("SND_AUDIOCODEC_MP3\n");
- compr->codec = FORMAT_MP3;
- break;
- case SND_AUDIOCODEC_AAC:
- pr_debug("SND_AUDIOCODEC_AAC\n");
- compr->codec = FORMAT_MPEG4_AAC;
- break;
- case SND_AUDIOCODEC_AC3: {
- char params_value[MAX_AC3_PARAM_SIZE];
- int *params_value_data = (int *)params_value;
- /* 36 is the max param length for ddp */
- int i;
- struct snd_dec_ddp *ddp =
- &compr->info.codec_param.codec.options.ddp;
- uint32_t params_length = 0;
-
- memset(params_value, 0, MAX_AC3_PARAM_SIZE);
- /* check integer overflow */
- if (ddp->params_length > UINT_MAX/sizeof(int)) {
- pr_err("%s: Integer overflow ddp->params_length %d\n",
- __func__, ddp->params_length);
- return -EINVAL;
- }
- params_length = ddp->params_length*sizeof(int);
- if (params_length > MAX_AC3_PARAM_SIZE) {
- /*MAX is 36*sizeof(int) this should not happen*/
- pr_err("%s: params_length(%d) is greater than %zd\n",
- __func__, params_length, MAX_AC3_PARAM_SIZE);
- return -EINVAL;
- }
- pr_debug("SND_AUDIOCODEC_AC3\n");
- compr->codec = FORMAT_AC3;
- pr_debug("params_length: %d\n", ddp->params_length);
- for (i = 0; i < params_length/sizeof(int); i++)
- pr_debug("params_value[%d]: %x\n", i,
- params_value_data[i]);
- for (i = 0; i < ddp->params_length/2; i++) {
- ddp->params_id[i] = params_value_data[2*i];
- ddp->params_value[i] = params_value_data[2*i+1];
- }
- if (atomic_read(&prtd->start)) {
- rc = msm_compr_send_ddp_cfg(prtd->audio_client,
- ddp);
- if (rc < 0)
- pr_err("%s: DDP CMD CFG failed\n",
- __func__);
- }
- break;
- }
- case SND_AUDIOCODEC_EAC3: {
- char params_value[MAX_AC3_PARAM_SIZE];
- int *params_value_data = (int *)params_value;
- /* 36 is the max param length for ddp */
- int i;
- struct snd_dec_ddp *ddp =
- &compr->info.codec_param.codec.options.ddp;
- uint32_t params_length = 0;
-
- memset(params_value, 0, MAX_AC3_PARAM_SIZE);
- /* check integer overflow */
- if (ddp->params_length > UINT_MAX/sizeof(int)) {
- pr_err("%s: Integer overflow ddp->params_length %d\n",
- __func__, ddp->params_length);
- return -EINVAL;
- }
- params_length = ddp->params_length*sizeof(int);
- if (params_length > MAX_AC3_PARAM_SIZE) {
- /*MAX is 36*sizeof(int) this should not happen*/
- pr_err("%s: params_length(%d) is greater than %zd\n",
- __func__, params_length, MAX_AC3_PARAM_SIZE);
- return -EINVAL;
- }
- pr_debug("SND_AUDIOCODEC_EAC3\n");
- compr->codec = FORMAT_EAC3;
- pr_debug("params_length: %d\n", ddp->params_length);
- for (i = 0; i < ddp->params_length; i++)
- pr_debug("params_value[%d]: %x\n", i,
- params_value_data[i]);
- for (i = 0; i < ddp->params_length/2; i++) {
- ddp->params_id[i] = params_value_data[2*i];
- ddp->params_value[i] = params_value_data[2*i+1];
- }
- if (atomic_read(&prtd->start)) {
- rc = msm_compr_send_ddp_cfg(prtd->audio_client,
- ddp);
- if (rc < 0)
- pr_err("%s: DDP CMD CFG failed\n",
- __func__);
- }
- break;
- }
- default:
- pr_debug("FORMAT_LINEAR_PCM\n");
- compr->codec = FORMAT_LINEAR_PCM;
- break;
- }
- return 0;
- case SNDRV_PCM_IOCTL1_RESET:
- pr_debug("SNDRV_PCM_IOCTL1_RESET\n");
- /* Flush only when session is started during CAPTURE,
- * while PLAYBACK has no such restriction.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
- (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- atomic_read(&prtd->start))) {
- if (atomic_read(&prtd->eos)) {
- prtd->cmd_interrupt = 1;
- wake_up(&the_locks.eos_wait);
- atomic_set(&prtd->eos, 0);
- }
-
- /* A unlikely race condition possible with FLUSH
- * DRAIN if ack is set by flush and reset by drain
- */
- prtd->cmd_ack = 0;
- rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
- if (rc < 0) {
- pr_err("%s: flush cmd failed rc=%d\n",
- __func__, rc);
- return rc;
- }
- rc = wait_event_timeout(the_locks.flush_wait,
- prtd->cmd_ack, 5 * HZ);
- if (!rc)
- pr_err("Flush cmd timeout\n");
- prtd->pcm_irq_pos = 0;
- }
- break;
- case SNDRV_COMPRESS_DRAIN:
- pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__);
- if (atomic_read(&prtd->pending_buffer)) {
- pr_debug("%s: no pending writes, drain would block\n",
- __func__);
- return -EWOULDBLOCK;
- }
-
- atomic_set(&prtd->eos, 1);
- atomic_set(&prtd->pending_buffer, 0);
- prtd->cmd_ack = 0;
- q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
- /* Wait indefinitely for DRAIN. Flush can also signal this*/
- rc = wait_event_interruptible(the_locks.eos_wait,
- (prtd->cmd_ack || prtd->cmd_interrupt));
-
- if (rc < 0)
- pr_err("EOS cmd interrupted\n");
- pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait\n", __func__);
-
- if (prtd->cmd_interrupt)
- rc = -EINTR;
-
- prtd->cmd_interrupt = 0;
- return rc;
- default:
- break;
- }
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-#ifdef CONFIG_COMPAT
-struct snd_enc_wma32 {
- u32 super_block_align; /* WMA Type-specific data */
- u32 encodeopt1;
- u32 encodeopt2;
-};
-
-struct snd_enc_vorbis32 {
- s32 quality;
- u32 managed;
- u32 max_bit_rate;
- u32 min_bit_rate;
- u32 downmix;
-};
-
-struct snd_enc_real32 {
- u32 quant_bits;
- u32 start_region;
- u32 num_regions;
-};
-
-struct snd_enc_flac32 {
- u32 num;
- u32 gain;
-};
-
-struct snd_enc_generic32 {
- u32 bw; /* encoder bandwidth */
- s32 reserved[15];
-};
-struct snd_dec_ddp32 {
- u32 params_length;
- u32 params_id[18];
- u32 params_value[18];
-};
-
-union snd_codec_options32 {
- struct snd_enc_wma32 wma;
- struct snd_enc_vorbis32 vorbis;
- struct snd_enc_real32 real;
- struct snd_enc_flac32 flac;
- struct snd_enc_generic32 generic;
- struct snd_dec_ddp32 ddp;
-};
-
-struct snd_codec32 {
- u32 id;
- u32 ch_in;
- u32 ch_out;
- u32 sample_rate;
- u32 bit_rate;
- u32 rate_control;
- u32 profile;
- u32 level;
- u32 ch_mode;
- u32 format;
- u32 align;
- union snd_codec_options32 options;
- u32 reserved[3];
-};
-
-struct snd_compressed_buffer32 {
- u32 fragment_size;
- u32 fragments;
-};
-
-struct snd_compr_params32 {
- struct snd_compressed_buffer32 buffer;
- struct snd_codec32 codec;
- u8 no_wake_mode;
-};
-
-struct snd_compr_caps32 {
- u32 num_codecs;
- u32 direction;
- u32 min_fragment_size;
- u32 max_fragment_size;
- u32 min_fragments;
- u32 max_fragments;
- u32 codecs[MAX_NUM_CODECS];
- u32 reserved[11];
-};
-struct snd_compr_tstamp32 {
- u32 byte_offset;
- u32 copied_total;
- compat_ulong_t pcm_frames;
- compat_ulong_t pcm_io_frames;
- u32 sampling_rate;
- compat_u64 timestamp;
-};
-enum {
- SNDRV_COMPRESS_TSTAMP32 = _IOR('C', 0x20, struct snd_compr_tstamp32),
- SNDRV_COMPRESS_GET_CAPS32 = _IOWR('C', 0x10, struct snd_compr_caps32),
- SNDRV_COMPRESS_SET_PARAMS32 =
- _IOW('C', 0x12, struct snd_compr_params32),
-};
-static int msm_compr_compat_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- int err = 0;
-
- switch (cmd) {
- case SNDRV_COMPRESS_TSTAMP32: {
- struct snd_compr_tstamp tstamp;
- struct snd_compr_tstamp32 tstamp32;
-
- memset(&tstamp, 0, sizeof(tstamp));
- memset(&tstamp32, 0, sizeof(tstamp32));
- cmd = SNDRV_COMPRESS_TSTAMP;
- err = msm_compr_ioctl_shared(substream, cmd, &tstamp);
- if (err) {
- pr_err("%s: COMPRESS_TSTAMP failed rc %d\n",
- __func__, err);
- goto bail_out;
- }
- tstamp32.byte_offset = tstamp.byte_offset;
- tstamp32.copied_total = tstamp.copied_total;
- tstamp32.pcm_frames = tstamp.pcm_frames;
- tstamp32.pcm_io_frames = tstamp.pcm_io_frames;
- tstamp32.sampling_rate = tstamp.sampling_rate;
- tstamp32.timestamp = tstamp.timestamp;
- if (copy_to_user(arg, &tstamp32, sizeof(tstamp32))) {
- pr_err("%s: copytouser failed COMPRESS_TSTAMP32\n",
- __func__);
- err = -EFAULT;
- }
- break;
- }
- case SNDRV_COMPRESS_GET_CAPS32: {
- struct snd_compr_caps caps;
- struct snd_compr_caps32 caps32;
- u32 i;
-
- memset(&caps, 0, sizeof(caps));
- memset(&caps32, 0, sizeof(caps32));
- cmd = SNDRV_COMPRESS_GET_CAPS;
- err = msm_compr_ioctl_shared(substream, cmd, &caps);
- if (err) {
- pr_err("%s: GET_CAPS failed rc %d\n",
- __func__, err);
- goto bail_out;
- }
- pr_debug("SNDRV_COMPRESS_GET_CAPS_32\n");
- if (!err && caps.num_codecs >= MAX_NUM_CODECS) {
- pr_err("%s: Invalid number of codecs\n", __func__);
- err = -EINVAL;
- goto bail_out;
- }
- caps32.direction = caps.direction;
- caps32.max_fragment_size = caps.max_fragment_size;
- caps32.max_fragments = caps.max_fragments;
- caps32.min_fragment_size = caps.min_fragment_size;
- caps32.num_codecs = caps.num_codecs;
- for (i = 0; i < caps.num_codecs; i++)
- caps32.codecs[i] = caps.codecs[i];
- if (copy_to_user(arg, &caps32, sizeof(caps32))) {
- pr_err("%s: copytouser failed COMPRESS_GETCAPS32\n",
- __func__);
- err = -EFAULT;
- }
- break;
- }
- case SNDRV_COMPRESS_SET_PARAMS32: {
- struct snd_compr_params32 params32;
- struct snd_compr_params params;
-
- memset(¶ms32, 0, sizeof(params32));
- memset(¶ms, 0, sizeof(params));
- cmd = SNDRV_COMPRESS_SET_PARAMS;
- if (copy_from_user(¶ms32, arg, sizeof(params32))) {
- pr_err("%s: copyfromuser failed SET_PARAMS32\n",
- __func__);
- err = -EFAULT;
- goto bail_out;
- }
- params.no_wake_mode = params32.no_wake_mode;
- params.codec.id = params32.codec.id;
- params.codec.ch_in = params32.codec.ch_in;
- params.codec.ch_out = params32.codec.ch_out;
- params.codec.sample_rate = params32.codec.sample_rate;
- params.codec.bit_rate = params32.codec.bit_rate;
- params.codec.rate_control = params32.codec.rate_control;
- params.codec.profile = params32.codec.profile;
- params.codec.level = params32.codec.level;
- params.codec.ch_mode = params32.codec.ch_mode;
- params.codec.format = params32.codec.format;
- params.codec.align = params32.codec.align;
-
- switch (params.codec.id) {
- case SND_AUDIOCODEC_WMA:
- case SND_AUDIOCODEC_WMA_PRO:
- params.codec.options.wma.encodeopt1 =
- params32.codec.options.wma.encodeopt1;
- params.codec.options.wma.encodeopt2 =
- params32.codec.options.wma.encodeopt2;
- params.codec.options.wma.super_block_align =
- params32.codec.options.wma.super_block_align;
- break;
- case SND_AUDIOCODEC_VORBIS:
- params.codec.options.vorbis.downmix =
- params32.codec.options.vorbis.downmix;
- params.codec.options.vorbis.managed =
- params32.codec.options.vorbis.managed;
- params.codec.options.vorbis.max_bit_rate =
- params32.codec.options.vorbis.max_bit_rate;
- params.codec.options.vorbis.min_bit_rate =
- params32.codec.options.vorbis.min_bit_rate;
- params.codec.options.vorbis.quality =
- params32.codec.options.vorbis.quality;
- break;
- case SND_AUDIOCODEC_REAL:
- params.codec.options.real.num_regions =
- params32.codec.options.real.num_regions;
- params.codec.options.real.quant_bits =
- params32.codec.options.real.quant_bits;
- params.codec.options.real.start_region =
- params32.codec.options.real.start_region;
- break;
- case SND_AUDIOCODEC_FLAC:
- params.codec.options.flac.gain =
- params32.codec.options.flac.gain;
- params.codec.options.flac.num =
- params32.codec.options.flac.num;
- break;
- case SND_AUDIOCODEC_DTS:
- case SND_AUDIOCODEC_DTS_PASS_THROUGH:
- case SND_AUDIOCODEC_DTS_LBR:
- case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
- case SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK:
- break;
- case SND_AUDIOCODEC_AC3:
- case SND_AUDIOCODEC_EAC3:
- params.codec.options.ddp.params_length =
- params32.codec.options.ddp.params_length;
- memcpy(params.codec.options.ddp.params_value,
- params32.codec.options.ddp.params_value,
- sizeof(params32.codec.options.ddp.params_value));
- memcpy(params.codec.options.ddp.params_id,
- params32.codec.options.ddp.params_id,
- sizeof(params32.codec.options.ddp.params_id));
- break;
- default:
- params.codec.options.generic.bw =
- params32.codec.options.generic.bw;
- break;
- }
- if (!err)
- err = msm_compr_ioctl_shared(substream, cmd, ¶ms);
- break;
- }
- default:
- err = msm_compr_ioctl_shared(substream, cmd, arg);
- }
-bail_out:
- return err;
-
-}
-#endif
-static int msm_compr_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- int err = 0;
-
- if (!substream) {
- pr_err("%s: Invalid params\n", __func__);
- return -EINVAL;
- }
- pr_debug("%s called with cmd = %d\n", __func__, cmd);
- switch (cmd) {
- case SNDRV_COMPRESS_TSTAMP: {
- struct snd_compr_tstamp tstamp;
-
- if (!arg) {
- pr_err("%s: Invalid params Tstamp\n", __func__);
- return -EINVAL;
- }
- err = msm_compr_ioctl_shared(substream, cmd, &tstamp);
- if (err)
- pr_err("%s: COMPRESS_TSTAMP failed rc %d\n",
- __func__, err);
- if (!err && copy_to_user(arg, &tstamp, sizeof(tstamp))) {
- pr_err("%s: copytouser failed COMPRESS_TSTAMP\n",
- __func__);
- err = -EFAULT;
- }
- break;
- }
- case SNDRV_COMPRESS_GET_CAPS: {
- struct snd_compr_caps cap;
-
- if (!arg) {
- pr_err("%s: Invalid params getcaps\n", __func__);
- return -EINVAL;
- }
- pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
- err = msm_compr_ioctl_shared(substream, cmd, &cap);
- if (err)
- pr_err("%s: GET_CAPS failed rc %d\n",
- __func__, err);
- if (!err && copy_to_user(arg, &cap, sizeof(cap))) {
- pr_err("%s: copytouser failed GET_CAPS\n",
- __func__);
- err = -EFAULT;
- }
- break;
- }
- case SNDRV_COMPRESS_SET_PARAMS: {
- struct snd_compr_params params;
-
- if (!arg) {
- pr_err("%s: Invalid params setparam\n", __func__);
- return -EINVAL;
- }
- if (copy_from_user(¶ms, arg,
- sizeof(struct snd_compr_params))) {
- pr_err("%s: SET_PARAMS\n", __func__);
- return -EFAULT;
- }
- err = msm_compr_ioctl_shared(substream, cmd, ¶ms);
- if (err)
- pr_err("%s: SET_PARAMS failed rc %d\n",
- __func__, err);
- break;
- }
- default:
- err = msm_compr_ioctl_shared(substream, cmd, arg);
- }
- return err;
-}
-
-static int msm_compr_restart(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
- struct audio_aio_write_param param;
- struct audio_buffer *buf = NULL;
- struct output_meta_data_st output_meta_data;
- int time_stamp_flag = 0;
- int buffer_length = 0;
-
- pr_debug("%s, trigger restart\n", __func__);
-
- if (runtime->render_flag & SNDRV_RENDER_STOPPED) {
- buf = prtd->audio_client->port[IN].buf;
- pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
- __func__, prtd->pcm_count, prtd->out_head);
- pr_debug("%s:writing buffer[%d] from 0x%08x\n",
- __func__, prtd->out_head,
- ((unsigned int)buf[0].phys
- + (prtd->out_head * prtd->pcm_count)));
-
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- time_stamp_flag = SET_TIMESTAMP;
- else
- time_stamp_flag = NO_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
- prtd->out_head * prtd->pcm_count),
- COMPRE_OUTPUT_METADATA_SIZE);
-
- buffer_length = output_meta_data.frame_size;
- pr_debug("meta_data_length: %d, frame_length: %d\n",
- output_meta_data.meta_data_length,
- output_meta_data.frame_size);
- pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
- output_meta_data.timestamp_msw,
- output_meta_data.timestamp_lsw);
-
- param.paddr = (unsigned long)buf[0].phys
- + (prtd->out_head * prtd->pcm_count)
- + output_meta_data.meta_data_length;
- param.len = buffer_length;
- param.msw_ts = output_meta_data.timestamp_msw;
- param.lsw_ts = output_meta_data.timestamp_lsw;
- param.flags = time_stamp_flag;
- param.uid = prtd->session_id;
- if (q6asm_async_write(prtd->audio_client,
- ¶m) < 0)
- pr_err("%s:q6asm_async_write failed\n",
- __func__);
- else
- prtd->out_head =
- (prtd->out_head + 1) & (runtime->periods - 1);
-
- runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
- return 0;
- }
- return 0;
-}
-
-static int msm_compr_volume_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int rc = 0;
- struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
- struct snd_pcm_substream *substream =
- vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- struct msm_audio *prtd;
- int volume = ucontrol->value.integer.value[0];
-
- pr_debug("%s: volume : %x\n", __func__, volume);
- if (!substream)
- return -ENODEV;
- if (!substream->runtime)
- return 0;
- prtd = substream->runtime->private_data;
- if (prtd)
- rc = compressed_set_volume(prtd, volume);
-
- return rc;
-}
-
-static int msm_compr_volume_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
- struct snd_pcm_substream *substream =
- vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- struct msm_audio *prtd;
-
- pr_debug("%s\n", __func__);
- if (!substream)
- return -ENODEV;
- if (!substream->runtime)
- return 0;
- prtd = substream->runtime->private_data;
- if (prtd)
- ucontrol->value.integer.value[0] = prtd->volume;
- return 0;
-}
-
-static int msm_compr_add_controls(struct snd_soc_pcm_runtime *rtd)
-{
- int ret = 0;
- struct snd_pcm *pcm = rtd->pcm;
- struct snd_pcm_volume *volume_info;
- struct snd_kcontrol *kctl;
-
- dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
- ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- NULL, 1, rtd->dai_link->id,
- &volume_info);
- if (ret < 0)
- return ret;
- kctl = volume_info->kctl;
- kctl->put = msm_compr_volume_ctl_put;
- kctl->get = msm_compr_volume_ctl_get;
- kctl->tlv.p = compr_rx_vol_gain;
- return 0;
-}
-
-static const struct snd_pcm_ops msm_compr_ops = {
- .open = msm_compr_open,
- .hw_params = msm_compr_hw_params,
- .close = msm_compr_close,
- .ioctl = msm_compr_ioctl,
- .prepare = msm_compr_prepare,
- .trigger = msm_compr_trigger,
- .pointer = msm_compr_pointer,
- .mmap = msm_compr_mmap,
- .restart = msm_compr_restart,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = msm_compr_compat_ioctl,
-#endif
-};
-
-static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- int ret = 0;
-
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- ret = msm_compr_add_controls(rtd);
- if (ret)
- pr_err("%s, kctl add failed\n", __func__);
- return ret;
-}
-
-static struct snd_soc_platform_driver msm_soc_platform = {
- .ops = &msm_compr_ops,
- .pcm_new = msm_asoc_pcm_new,
-};
-
-static int msm_compr_probe(struct platform_device *pdev)
-{
-
- dev_info(&pdev->dev, "%s: dev name %s\n",
- __func__, dev_name(&pdev->dev));
-
- return snd_soc_register_platform(&pdev->dev,
- &msm_soc_platform);
-}
-
-static int msm_compr_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static const struct of_device_id msm_compr_dt_match[] = {
- {.compatible = "qcom,msm-compr-dsp"},
- {}
-};
-MODULE_DEVICE_TABLE(of, msm_compr_dt_match);
-
-static struct platform_driver msm_compr_driver = {
- .driver = {
- .name = "msm-compr-dsp",
- .owner = THIS_MODULE,
- .of_match_table = msm_compr_dt_match,
- },
- .probe = msm_compr_probe,
- .remove = msm_compr_remove,
-};
-
-static int __init msm_soc_platform_init(void)
-{
- init_waitqueue_head(&the_locks.enable_wait);
- init_waitqueue_head(&the_locks.eos_wait);
- init_waitqueue_head(&the_locks.write_wait);
- init_waitqueue_head(&the_locks.read_wait);
- init_waitqueue_head(&the_locks.flush_wait);
-
- return platform_driver_register(&msm_compr_driver);
-}
-module_init(msm_soc_platform_init);
-
-static void __exit msm_soc_platform_exit(void)
-{
- platform_driver_unregister(&msm_compr_driver);
-}
-module_exit(msm_soc_platform_exit);
-
-MODULE_DESCRIPTION("PCM module platform driver");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
deleted file mode 100644
index d6e3ec6..0000000
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MSM_COMPR_H
-#define _MSM_COMPR_H
-#include <sound/apr_audio-v2.h>
-#include <sound/q6asm-v2.h>
-#include <sound/compress_params.h>
-#include <sound/compress_offload.h>
-#include <sound/compress_driver.h>
-
-#include "msm-pcm-q6-v2.h"
-
-struct compr_info {
- struct snd_compr_caps compr_cap;
- struct snd_compr_codec_caps codec_caps;
- struct snd_compr_params codec_param;
-};
-
-struct compr_audio {
- struct msm_audio prtd;
- struct compr_info info;
- uint32_t codec;
-};
-
-#endif /*_MSM_COMPR_H*/
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 9e5fc16..42dfbeb 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -375,6 +375,105 @@
*/
BPF_FUNC_probe_write_user,
+ /**
+ * int bpf_skb_change_tail(skb, len, flags)
+ * The helper will resize the skb to the given new size, to be used f.e.
+ * with control messages.
+ * @skb: pointer to skb
+ * @len: new skb length
+ * @flags: reserved
+ * Return: 0 on success or negative error
+ */
+ BPF_FUNC_skb_change_tail,
+
+ /**
+ * int bpf_skb_pull_data(skb, len)
+ * The helper will pull in non-linear data in case the skb is non-linear
+ * and not all of len are part of the linear section. Only needed for
+ * read/write with direct packet access.
+ * @skb: pointer to skb
+ * @len: len to make read/writeable
+ * Return: 0 on success or negative error
+ */
+ BPF_FUNC_skb_pull_data,
+
+ /**
+ * s64 bpf_csum_update(skb, csum)
+ * Adds csum into skb->csum in case of CHECKSUM_COMPLETE.
+ * @skb: pointer to skb
+ * @csum: csum to add
+ * Return: csum on success or negative error
+ */
+ BPF_FUNC_csum_update,
+
+ /**
+ * void bpf_set_hash_invalid(skb)
+ * Invalidate current skb->hash.
+ * @skb: pointer to skb
+ */
+ BPF_FUNC_set_hash_invalid,
+
+ /**
+ * int bpf_get_numa_node_id()
+ * Return: Id of current NUMA node.
+ */
+ BPF_FUNC_get_numa_node_id,
+
+ /**
+ * int bpf_skb_change_head()
+ * Grows headroom of skb and adjusts MAC header offset accordingly.
+ * Will extends/reallocae as required automatically.
+ * May change skb data pointer and will thus invalidate any check
+ * performed for direct packet access.
+ * @skb: pointer to skb
+ * @len: length of header to be pushed in front
+ * @flags: Flags (unused for now)
+ * Return: 0 on success or negative error
+ */
+ BPF_FUNC_skb_change_head,
+
+ /**
+ * int bpf_xdp_adjust_head(xdp_md, delta)
+ * Adjust the xdp_md.data by delta
+ * @xdp_md: pointer to xdp_md
+ * @delta: An positive/negative integer to be added to xdp_md.data
+ * Return: 0 on success or negative on error
+ */
+ BPF_FUNC_xdp_adjust_head,
+
+ /**
+ * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
+ * Copy a NUL terminated string from unsafe address. In case the string
+ * length is smaller than size, the target is not padded with further NUL
+ * bytes. In case the string length is larger than size, just count-1
+ * bytes are copied and the last byte is set to NUL.
+ * @dst: destination address
+ * @size: maximum number of bytes to copy, including the trailing NUL
+ * @unsafe_ptr: unsafe address
+ * Return:
+ * > 0 length of the string including the trailing NUL on success
+ * < 0 error
+ */
+ BPF_FUNC_probe_read_str,
+
+ /**
+ * u64 bpf_get_socket_cookie(skb)
+ * Get the cookie for the socket stored inside sk_buff.
+ * @skb: pointer to skb
+ * Return: 8 Bytes non-decreasing number on success or 0 if
+ * the socket
+ * field is missing inside sk_buff
+ */
+ BPF_FUNC_get_socket_cookie,
+
+ /**
+ * u32 bpf_get_socket_uid(skb)
+ * Get the owner uid of the socket stored inside sk_buff.
+ * @skb: pointer to skb
+ * Return: uid of the socket owner on success or overflowuid if failed.
+ */
+ BPF_FUNC_get_socket_uid,
+
__BPF_FUNC_MAX_ID,
};