Merge "drm: msm: add support for a custom hotplug event"
diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
index c467327..54bbf25 100644
--- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt
+++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
@@ -32,6 +32,8 @@
- clocks : List of phandle and clock specifier pairs
- clock-names : List of clock input name strings sorted in the same
order as the clocks property.
+- qcom,keep_radio_on_during_sleep: Boolean flag to indicate if to suspend to d3hot
+ instead of turning off the device
Example:
wil6210: qcom,wil6210 {
@@ -56,5 +58,6 @@
clocks = <&clock_gcc clk_rf_clk3>,
<&clock_gcc clk_rf_clk3_pin>;
clock-names = "rf_clk3_clk", "rf_clk3_pin_clk";
+ qcom,keep_radio_on_during_sleep;
};
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index aefd42c..5cf2cb8 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -132,8 +132,6 @@
power collapse feature available or not.
- qcom,sde-has-mixer-gc: Boolean property to indicate if mixer has gamma correction
feature available or not.
-- qcom,sde-has-cdp: Boolean property to indicate if cdp feature is
- available or not.
- qcom,sde-sspp-clk-ctrl: Array of offsets describing clk control
offsets for dynamic clock gating. 1st value
in the array represents offset of the control
@@ -236,6 +234,8 @@
of (pps, OT limit), where pps is pixel per second and
OT limit is the write limit to apply if the given
pps is not exceeded.
+- qcom,sde-vbif-memtype-0: Array of u32 vbif memory type settings, group 0
+- qcom,sde-vbif-memtype-1: Array of u32 vbif memory type settings, group 1
- qcom,sde-wb-id: Array of writeback ids corresponding to the
offsets defined in property: qcom,sde-wb-off.
- qcom,sde-wb-clk-ctrl: Array of 2 cell property describing clk control
@@ -319,6 +319,9 @@
<fill level, lut hi, lut lo> in ascending fill level
indicating the qos luts for cwb on sspp.
Zero fill level on the last entry identifies the default lut.
+- qcom,sde-cdp-setting: Array of 2 cell property, with a format of
+ <read enable, write enable> for cdp use cases in
+ order of <real_time>, and <non_real_time>.
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
@@ -447,7 +450,6 @@
qcom,sde-ubwc-static = <0x100>;
qcom,sde-ubwc-swizzle = <0>;
qcom,sde-panic-per-pipe;
- qcom,sde-has-cdp;
qcom,sde-has-src-split;
qcom,sde-has-dim-layer;
qcom,sde-sspp-src-size = <0x100>;
@@ -517,6 +519,8 @@
qcom,sde-qos-lut-cwb =
<0 0x75300000 0x00000000>;
+ qcom,sde-cdp-setting = <1 1>, <1 0>;
+
qcom,sde-vbif-off = <0 0>;
qcom,sde-vbif-id = <0 1>;
qcom,sde-vbif-default-ot-rd-limit = <32>;
@@ -525,6 +529,8 @@
<124416000 4>, <248832000 16>;
qcom,sde-vbif-dynamic-ot-wr-limit = <62208000 2>,
<124416000 4>, <248832000 16>;
+ qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>;
+ qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>;
qcom,sde-dram-channels = <2>;
qcom,sde-num-nrt-paths = <1>;
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 375eaf2..1394fd3 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -74,14 +74,6 @@
address size faults are due to a fundamental programming
error from which we don't care about recovering anyways.
-- qcom,skip-init : Disable resetting configuration for all context banks
- during device reset. This is useful for targets where
- some context banks are dedicated to other execution
- environments outside of Linux and those other EEs are
- programming their own stream match tables, SCTLR, etc.
- Without setting this option we will trample on their
- configuration.
-
- qcom,dynamic : Allow dynamic domains to be attached. This is only
useful if the upstream hardware is capable of switching
between multiple domains within a single context bank.
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 442434d..0c2ae5f 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -63,7 +63,6 @@
<0x150c2000 0x20>;
reg-names = "base", "tcu-base";
#iommu-cells = <2>;
- qcom,skip-init;
qcom,use-3-lvl-tables;
#global-interrupts = <1>;
#size-cells = <1>;
@@ -330,10 +329,10 @@
apps_iommu_test_device {
compatible = "iommu-debug-test";
/*
- * This SID belongs to QUP1-GSI. We can't use a fake SID for
+ * This SID belongs to TSIF. We can't use a fake SID for
* the apps_smmu device.
*/
- iommus = <&apps_smmu 0x16 0>;
+ iommus = <&apps_smmu 0x20 0>;
};
apps_iommu_coherent_test_device {
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 e9b71b9..4b7a680 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_nt35597_truly_dsc_cmd_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 73df071..fcf6ad1 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_nt35597_truly_dsc_cmd_display {
+&dsi_dual_nt35597_truly_cmd_display {
/delete-property/ qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index a1e0e4f..5e370d6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -298,7 +298,7 @@
qcom,platform-reset-gpio = <&tlmm 6 0>;
};
-&dsi_nt35597_truly_dsc_cmd_display {
+&dsi_dual_nt35597_truly_cmd_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 85bec57..b5c471f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -150,7 +150,7 @@
qcom,platform-reset-gpio = <&tlmm 6 0>;
};
-&dsi_nt35597_truly_dsc_cmd_display {
+&dsi_dual_nt35597_truly_cmd_display {
qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index c350800..2a29283 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -136,10 +136,40 @@
qcom,sde-vbif-off = <0>;
qcom,sde-vbif-size = <0x1040>;
qcom,sde-vbif-id = <0>;
+ qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>;
+ qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>;
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-danger-lut = <0x0000000f 0x0000ffff 0x00000000
+ 0x00000000>;
+ qcom,sde-safe-lut = <0xfffc 0xff00 0xffff 0xffff>;
+ qcom,sde-qos-lut-linear =
+ <4 0x00000000 0x00000357>,
+ <5 0x00000000 0x00003357>,
+ <6 0x00000000 0x00023357>,
+ <7 0x00000000 0x00223357>,
+ <8 0x00000000 0x02223357>,
+ <9 0x00000000 0x22223357>,
+ <10 0x00000002 0x22223357>,
+ <11 0x00000022 0x22223357>,
+ <12 0x00000222 0x22223357>,
+ <13 0x00002222 0x22223357>,
+ <14 0x00012222 0x22223357>,
+ <0 0x00112222 0x22223357>;
+ qcom,sde-qos-lut-macrotile =
+ <10 0x00000003 0x44556677>,
+ <11 0x00000033 0x44556677>,
+ <12 0x00000233 0x44556677>,
+ <13 0x00002233 0x44556677>,
+ <14 0x00012233 0x44556677>,
+ <0 0x00112233 0x44556677>;
+ qcom,sde-qos-lut-nrt =
+ <0 0x00000000 0x00000000>;
+ qcom,sde-qos-lut-cwb =
+ <0 0x75300000 0x00000000>;
+
qcom,sde-inline-rotator = <&mdss_rotator 0>;
qcom,sde-reg-dma-off = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index a38ac59..75aa3b1 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -4203,10 +4203,12 @@
};
&vcodec0_gdsc {
+ qcom,support-hw-trigger;
status = "ok";
};
&vcodec1_gdsc {
+ qcom,support-hw-trigger;
status = "ok";
};
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index adbabea..03d3ab9 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -87,7 +87,7 @@
};
static struct pll_vco fabia_vco[] = {
- { 250000000, 2000000000, 0 },
+ { 249600000, 2000000000, 0 },
{ 125000000, 1000000000, 1 },
};
@@ -278,6 +278,7 @@
};
static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0),
F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0),
@@ -316,7 +317,6 @@
{ }
};
-
static struct clk_rcg2 cam_cc_cci_clk_src = {
.cmd_rcgr = 0xb0d8,
.mnd_width = 8,
@@ -341,7 +341,7 @@
F(19200000, P_BI_TCXO, 1, 0, 0),
F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0),
F(320000000, P_CAM_CC_PLL2_OUT_ODD, 3, 0, 0),
- F(384000000, P_CAM_CC_PLL2_OUT_ODD, 2.5, 0, 0),
+ F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
{ }
};
@@ -430,6 +430,7 @@
};
static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(50000000, P_CAM_CC_PLL0_OUT_EVEN, 12, 0, 0),
F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0),
@@ -490,13 +491,22 @@
},
};
+static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0),
+ F(400000000, P_CAM_CC_PLL0_OUT_EVEN, 1.5, 0, 0),
+ F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0),
+ F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0),
+ { }
+};
+
static struct clk_rcg2 cam_cc_icp_clk_src = {
.cmd_rcgr = 0xb088,
.mnd_width = 0,
.hid_width = 5,
.enable_safe_config = true,
.parent_map = cam_cc_parent_map_0,
- .freq_tbl = ftbl_cam_cc_fd_core_clk_src,
+ .freq_tbl = ftbl_cam_cc_icp_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "cam_cc_icp_clk_src",
.parent_names = cam_cc_parent_names_0,
@@ -513,6 +523,7 @@
};
static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
F(320000000, P_CAM_CC_PLL2_OUT_EVEN, 1.5, 0, 0),
F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0),
@@ -544,6 +555,7 @@
};
static const struct freq_tbl ftbl_cam_cc_ife_0_csid_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0),
F(384000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0),
F(538666667, P_CAM_CC_PLL1_OUT_EVEN, 1.5, 0, 0),
@@ -655,6 +667,7 @@
};
static const struct freq_tbl ftbl_cam_cc_ipe_0_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
F(240000000, P_CAM_CC_PLL0_OUT_EVEN, 2.5, 0, 0),
F(404000000, P_CAM_CC_PLL1_OUT_EVEN, 2, 0, 0),
@@ -733,6 +746,7 @@
};
static const struct freq_tbl ftbl_cam_cc_lrme_clk_src[] = {
+ F(19200000, P_BI_TCXO, 1, 0, 0),
F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0),
F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0),
F(384000000, P_CAM_CC_PLL2_OUT_ODD, 2.5, 0, 0),
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 2742ab3..4e0711d 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -197,7 +197,7 @@
};
static struct pll_vco fabia_vco[] = {
- { 250000000, 2000000000, 0 },
+ { 249600000, 2000000000, 0 },
{ 125000000, 1000000000, 1 },
};
@@ -790,8 +790,8 @@
F(400000, P_BI_TCXO, 12, 1, 4),
F(9600000, P_BI_TCXO, 2, 0, 0),
F(19200000, P_BI_TCXO, 1, 0, 0),
- F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
- F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+ F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
+ F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
{ }
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 3d6dc32..c2ef28d 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -294,6 +294,7 @@
* @bypass_irqreg: Bypass irq register/unregister if non-zero
* @wbdone_complete: for wbdone irq synchronization
* @wb_cfg: Writeback hardware configuration
+ * @cdp_cfg: Writeback CDP configuration
* @intf_cfg: Interface hardware configuration
* @wb_roi: Writeback region-of-interest
* @wb_fmt: Writeback pixel format
@@ -315,6 +316,7 @@
u32 bypass_irqreg;
struct completion wbdone_complete;
struct sde_hw_wb_cfg wb_cfg;
+ struct sde_hw_wb_cdp_cfg cdp_cfg;
struct sde_hw_intf_cfg intf_cfg;
struct sde_rect wb_roi;
const struct sde_format *wb_fmt;
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 385c610..1657b9b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -248,16 +248,18 @@
struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
struct sde_hw_wb *hw_wb;
struct sde_hw_wb_cfg *wb_cfg;
+ struct sde_hw_wb_cdp_cfg *cdp_cfg;
const struct msm_format *format;
int ret, mmu_id;
- if (!phys_enc) {
+ if (!phys_enc || !phys_enc->sde_kms || !phys_enc->sde_kms->catalog) {
SDE_ERROR("invalid encoder\n");
return;
}
hw_wb = wb_enc->hw_wb;
wb_cfg = &wb_enc->wb_cfg;
+ cdp_cfg = &wb_enc->cdp_cfg;
memset(wb_cfg, 0, sizeof(struct sde_hw_wb_cfg));
wb_cfg->intf_mode = phys_enc->intf_mode;
@@ -325,6 +327,21 @@
if (hw_wb->ops.setup_outformat)
hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
+ if (hw_wb->ops.setup_cdp) {
+ memset(cdp_cfg, 0, sizeof(struct sde_hw_wb_cdp_cfg));
+
+ cdp_cfg->enable = phys_enc->sde_kms->catalog->perf.cdp_cfg
+ [SDE_PERF_CDP_USAGE_NRT].wr_enable;
+ cdp_cfg->ubwc_meta_enable =
+ SDE_FORMAT_IS_UBWC(wb_cfg->dest.format);
+ cdp_cfg->tile_amortize_enable =
+ SDE_FORMAT_IS_UBWC(wb_cfg->dest.format) ||
+ SDE_FORMAT_IS_TILE(wb_cfg->dest.format);
+ cdp_cfg->preload_ahead = SDE_WB_CDP_PRELOAD_AHEAD_64;
+
+ hw_wb->ops.setup_cdp(hw_wb, cdp_cfg);
+ }
+
if (hw_wb->ops.setup_outaddress)
hw_wb->ops.setup_outaddress(hw_wb, wb_cfg);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 9f1b6cb..306bb86 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -136,7 +136,6 @@
QSEED_TYPE,
CSC_TYPE,
PANIC_PER_PIPE,
- CDP,
SRC_SPLIT,
DIM_LAYER,
SMART_DMA_REV,
@@ -165,6 +164,7 @@
PERF_QOS_LUT_MACROTILE,
PERF_QOS_LUT_NRT,
PERF_QOS_LUT_CWB,
+ PERF_CDP_SETTING,
PERF_PROP_MAX,
};
@@ -290,6 +290,8 @@
VBIF_DYNAMIC_OT_WR_LIMIT,
VBIF_QOS_RT_REMAP,
VBIF_QOS_NRT_REMAP,
+ VBIF_MEMTYPE_0,
+ VBIF_MEMTYPE_1,
VBIF_PROP_MAX,
};
@@ -349,7 +351,6 @@
{QSEED_TYPE, "qcom,sde-qseed-type", false, PROP_TYPE_STRING},
{CSC_TYPE, "qcom,sde-csc-type", false, PROP_TYPE_STRING},
{PANIC_PER_PIPE, "qcom,sde-panic-per-pipe", false, PROP_TYPE_BOOL},
- {CDP, "qcom,sde-has-cdp", false, PROP_TYPE_BOOL},
{SRC_SPLIT, "qcom,sde-has-src-split", false, PROP_TYPE_BOOL},
{DIM_LAYER, "qcom,sde-has-dim-layer", false, PROP_TYPE_BOOL},
{SMART_DMA_REV, "qcom,sde-smart-dma-rev", false, PROP_TYPE_STRING},
@@ -391,6 +392,8 @@
PROP_TYPE_U32_ARRAY},
{PERF_QOS_LUT_CWB, "qcom,sde-qos-lut-cwb", false,
PROP_TYPE_U32_ARRAY},
+ {PERF_CDP_SETTING, "qcom,sde-cdp-setting", false,
+ PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type sspp_prop[] = {
@@ -529,6 +532,8 @@
PROP_TYPE_U32_ARRAY},
{VBIF_QOS_NRT_REMAP, "qcom,sde-vbif-qos-nrt-remap", false,
PROP_TYPE_U32_ARRAY},
+ {VBIF_MEMTYPE_0, "qcom,sde-vbif-memtype-0", false, PROP_TYPE_U32_ARRAY},
+ {VBIF_MEMTYPE_1, "qcom,sde-vbif-memtype-1", false, PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type reg_dma_prop[REG_DMA_PROP_MAX] = {
@@ -1061,6 +1066,9 @@
set_bit(SDE_SSPP_SRC, &sspp->features);
+ if (sde_cfg->has_cdp)
+ set_bit(SDE_SSPP_CDP, &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) {
@@ -1500,6 +1508,9 @@
set_bit(SDE_WB_TRAFFIC_SHAPER, &wb->features);
set_bit(SDE_WB_YUV_CONFIG, &wb->features);
+ if (sde_cfg->has_cdp)
+ set_bit(SDE_WB_CDP, &wb->features);
+
set_bit(SDE_WB_QOS, &wb->features);
if (sde_cfg->vbif_qos_nlvl == 8)
set_bit(SDE_WB_QOS_8LVL, &wb->features);
@@ -1970,6 +1981,16 @@
if (rc)
goto end;
+ rc = _validate_dt_entry(np, &vbif_prop[VBIF_MEMTYPE_0], 1,
+ &prop_count[VBIF_MEMTYPE_0], NULL);
+ if (rc)
+ goto end;
+
+ rc = _validate_dt_entry(np, &vbif_prop[VBIF_MEMTYPE_1], 1,
+ &prop_count[VBIF_MEMTYPE_1], NULL);
+ if (rc)
+ goto end;
+
sde_cfg->vbif_count = off_count;
rc = _read_dt_entry(np, vbif_prop, ARRAY_SIZE(vbif_prop), prop_count,
@@ -2118,6 +2139,19 @@
if (vbif->qos_rt_tbl.npriority_lvl ||
vbif->qos_nrt_tbl.npriority_lvl)
set_bit(SDE_VBIF_QOS_REMAP, &vbif->features);
+
+ vbif->memtype_count = prop_count[VBIF_MEMTYPE_0] +
+ prop_count[VBIF_MEMTYPE_1];
+ if (vbif->memtype_count > MAX_XIN_COUNT) {
+ vbif->memtype_count = 0;
+ SDE_ERROR("too many memtype defs, ignoring entries\n");
+ }
+ for (j = 0, k = 0; j < prop_count[VBIF_MEMTYPE_0]; j++)
+ vbif->memtype[k++] = PROP_VALUE_ACCESS(
+ prop_value, VBIF_MEMTYPE_0, j);
+ for (j = 0; j < prop_count[VBIF_MEMTYPE_1]; j++)
+ vbif->memtype[k++] = PROP_VALUE_ACCESS(
+ prop_value, VBIF_MEMTYPE_1, j);
}
end:
@@ -2420,6 +2454,11 @@
if (rc)
goto freeprop;
+ rc = _validate_dt_entry(np, &sde_perf_prop[PERF_CDP_SETTING], 1,
+ &prop_count[PERF_CDP_SETTING], NULL);
+ if (rc)
+ goto freeprop;
+
rc = _read_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop),
prop_count, prop_exists, prop_value);
if (rc)
@@ -2559,6 +2598,27 @@
cfg->perf.qos_lut_tbl[j].nentry = count;
}
+ if (prop_exists[PERF_CDP_SETTING]) {
+ const u32 prop_size = 2;
+ u32 count = prop_count[PERF_CDP_SETTING] / prop_size;
+
+ count = min_t(u32, count, SDE_PERF_CDP_USAGE_MAX);
+
+ for (j = 0; j < count; j++) {
+ cfg->perf.cdp_cfg[j].rd_enable =
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_CDP_SETTING, j * prop_size);
+ cfg->perf.cdp_cfg[j].wr_enable =
+ PROP_VALUE_ACCESS(prop_value,
+ PERF_CDP_SETTING, j * prop_size + 1);
+ SDE_DEBUG("cdp usage:%d rd:%d wr:%d\n",
+ j, cfg->perf.cdp_cfg[j].rd_enable,
+ cfg->perf.cdp_cfg[j].wr_enable);
+ }
+
+ cfg->has_cdp = true;
+ }
+
freeprop:
kfree(prop_value);
end:
@@ -2760,6 +2820,10 @@
if (rc)
goto end;
+ rc = sde_perf_parse_dt(np, sde_cfg);
+ if (rc)
+ goto end;
+
rc = sde_rot_parse_dt(np, sde_cfg);
if (rc)
goto end;
@@ -2810,10 +2874,6 @@
if (rc)
goto end;
- rc = sde_perf_parse_dt(np, sde_cfg);
- if (rc)
- goto end;
-
return sde_cfg;
end:
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 7d1c180..beff43c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -60,6 +60,8 @@
#define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16)
#define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF)
+#define MAX_XIN_COUNT 16
+
/**
* Supported UBWC feature versions
*/
@@ -79,7 +81,6 @@
* @SDE_MDP_UBWC_1_0, This chipsets supports Universal Bandwidth
* compression initial revision
* @SDE_MDP_UBWC_1_5, Universal Bandwidth compression version 1.5
- * @SDE_MDP_CDP, Client driven prefetch
* @SDE_MDP_MAX Maximum value
*/
@@ -89,7 +90,6 @@
SDE_MDP_BWC,
SDE_MDP_UBWC_1_0,
SDE_MDP_UBWC_1_5,
- SDE_MDP_CDP,
SDE_MDP_MAX
};
@@ -114,6 +114,7 @@
* @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_CDP Supports client driven prefetch
* @SDE_SSPP_MAX maximum value
*/
enum {
@@ -136,6 +137,7 @@
SDE_SSPP_SBUF,
SDE_SSPP_TS_PREFILL,
SDE_SSPP_TS_PREFILL_REC1,
+ SDE_SSPP_CDP,
SDE_SSPP_MAX
};
@@ -245,6 +247,7 @@
* the destination image
* @SDE_WB_QOS, Writeback supports QoS control, danger/safe/creq
* @SDE_WB_QOS_8LVL, Writeback supports 8-level QoS control
+ * @SDE_WB_CDP Writeback supports client driven prefetch
* @SDE_WB_MAX maximum value
*/
enum {
@@ -262,6 +265,7 @@
SDE_WB_XY_ROI_OFFSET,
SDE_WB_QOS,
SDE_WB_QOS_8LVL,
+ SDE_WB_CDP,
SDE_WB_MAX
};
@@ -703,6 +707,8 @@
* @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
+ * @memtype_count number of defined memtypes
+ * @memtype array of xin memtype definitions
*/
struct sde_vbif_cfg {
SDE_HW_BLK_INFO;
@@ -713,6 +719,8 @@
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;
+ u32 memtype_count;
+ u32 memtype[MAX_XIN_COUNT];
};
/**
* struct sde_reg_dma_cfg - information of lut dma blocks
@@ -729,6 +737,27 @@
};
/**
+ * Define CDP use cases
+ * @SDE_PERF_CDP_UDAGE_RT: real-time use cases
+ * @SDE_PERF_CDP_USAGE_NRT: non real-time use cases such as WFD
+ */
+enum {
+ SDE_PERF_CDP_USAGE_RT,
+ SDE_PERF_CDP_USAGE_NRT,
+ SDE_PERF_CDP_USAGE_MAX
+};
+
+/**
+ * struct sde_perf_cdp_cfg - define CDP use case configuration
+ * @rd_enable: true if read pipe CDP is enabled
+ * @wr_enable: true if write pipe CDP is enabled
+ */
+struct sde_perf_cdp_cfg {
+ bool rd_enable;
+ bool wr_enable;
+};
+
+/**
* struct sde_perf_cfg - performance control settings
* @max_bw_low low threshold of maximum bandwidth (kbps)
* @max_bw_high high threshold of maximum bandwidth (kbps)
@@ -748,6 +777,7 @@
* @safe_lut_tbl: LUT tables for safe signals
* @danger_lut_tbl: LUT tables for danger signals
* @qos_lut_tbl: LUT tables for QoS signals
+ * @cdp_cfg cdp use case configurations
*/
struct sde_perf_cfg {
u32 max_bw_low;
@@ -768,6 +798,7 @@
u32 safe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
u32 danger_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
struct sde_qos_lut_tbl qos_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
+ struct sde_perf_cdp_cfg cdp_cfg[SDE_PERF_CDP_USAGE_MAX];
};
/**
@@ -785,7 +816,7 @@
* @csc_type csc or csc_10bit support.
* @smart_dma_rev Supported version of SmartDMA feature.
* @has_src_split source split feature status
- * @has_cdp Client driver prefetch feature status
+ * @has_cdp Client driven prefetch feature status
* @has_wb_ubwc UBWC feature supported on WB
* @ubwc_version UBWC feature version (0x0 for not supported)
* @has_sbuf indicate if stream buffer is available
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index a9a493f..bb9f9c0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -82,6 +82,7 @@
#define SSPP_SW_PIX_EXT_C3_TB 0x124
#define SSPP_SW_PIX_EXT_C3_REQ_PIXELS 0x128
#define SSPP_TRAFFIC_SHAPER 0x130
+#define SSPP_CDP_CNTL 0x134
#define SSPP_UBWC_ERROR_STATUS 0x138
#define SSPP_TRAFFIC_SHAPER_PREFILL 0x150
#define SSPP_TRAFFIC_SHAPER_REC1_PREFILL 0x154
@@ -1102,6 +1103,30 @@
SDE_REG_WRITE(&ctx->hw, ts_prefill_offset, ts_count);
}
+static void sde_hw_sspp_setup_cdp(struct sde_hw_pipe *ctx,
+ struct sde_hw_pipe_cdp_cfg *cfg)
+{
+ u32 idx;
+ u32 cdp_cntl = 0;
+
+ if (!ctx || !cfg)
+ return;
+
+ if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+ return;
+
+ if (cfg->enable)
+ cdp_cntl |= BIT(0);
+ if (cfg->ubwc_meta_enable)
+ cdp_cntl |= BIT(1);
+ if (cfg->tile_amortize_enable)
+ cdp_cntl |= BIT(2);
+ if (cfg->preload_ahead == SDE_SSPP_CDP_PRELOAD_AHEAD_64)
+ cdp_cntl |= BIT(3);
+
+ SDE_REG_WRITE(&ctx->hw, SSPP_CDP_CNTL, cdp_cntl);
+}
+
static void _setup_layer_ops(struct sde_hw_pipe *c,
unsigned long features)
{
@@ -1163,6 +1188,9 @@
c->ops.setup_sys_cache = sde_hw_sspp_setup_sys_cache;
c->ops.get_sbuf_status = sde_hw_sspp_get_sbuf_status;
}
+
+ if (test_bit(SDE_SSPP_CDP, &features))
+ c->ops.setup_cdp = sde_hw_sspp_setup_cdp;
}
static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 44b7ea9..d52c0e5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -308,6 +308,30 @@
};
/**
+ * enum CDP preload ahead address size
+ */
+enum {
+ SDE_SSPP_CDP_PRELOAD_AHEAD_32,
+ SDE_SSPP_CDP_PRELOAD_AHEAD_64
+};
+
+/**
+ * struct sde_hw_pipe_cdp_cfg : CDP configuration
+ * @enable: true to enable CDP
+ * @ubwc_meta_enable: true to enable ubwc metadata preload
+ * @tile_amortize_enable: true to enable amortization control for tile format
+ * @preload_ahead: number of request to preload ahead
+ * SDE_SSPP_CDP_PRELOAD_AHEAD_32,
+ * SDE_SSPP_CDP_PRELOAD_AHEAD_64
+ */
+struct sde_hw_pipe_cdp_cfg {
+ bool enable;
+ bool ubwc_meta_enable;
+ bool tile_amortize_enable;
+ u32 preload_ahead;
+};
+
+/**
* enum system cache rotation operation mode
*/
enum {
@@ -574,6 +598,14 @@
void (*setup_ts_prefill)(struct sde_hw_pipe *ctx,
struct sde_hw_pipe_ts_cfg *cfg,
enum sde_sspp_multirect_index index);
+
+ /**
+ * setup_cdp - setup client driven prefetch
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to cdp configuration
+ */
+ void (*setup_cdp)(struct sde_hw_pipe *ctx,
+ struct sde_hw_pipe_cdp_cfg *cfg);
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index 9b9763a..b5c273a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -31,11 +31,43 @@
#define VBIF_IN_WR_LIM_CONF2 0x00C8
#define VBIF_OUT_RD_LIM_CONF0 0x00D0
#define VBIF_OUT_WR_LIM_CONF0 0x00D4
+#define VBIF_OUT_AXI_AMEMTYPE_CONF0 0x0160
+#define VBIF_OUT_AXI_AMEMTYPE_CONF1 0x0164
#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_mem_type(struct sde_hw_vbif *vbif,
+ u32 xin_id, u32 value)
+{
+ struct sde_hw_blk_reg_map *c;
+ u32 reg_off;
+ u32 bit_off;
+ u32 reg_val;
+
+ /*
+ * Assume 4 bits per bit field, 8 fields per 32-bit register so
+ * 16 bit fields maximum across two registers
+ */
+ if (!vbif || xin_id >= MAX_XIN_COUNT || xin_id >= 16)
+ return;
+
+ c = &vbif->hw;
+
+ if (xin_id >= 8) {
+ xin_id -= 8;
+ reg_off = VBIF_OUT_AXI_AMEMTYPE_CONF1;
+ } else {
+ reg_off = VBIF_OUT_AXI_AMEMTYPE_CONF0;
+ }
+ bit_off = (xin_id & 0x7) * 4;
+ reg_val = SDE_REG_READ(c, reg_off);
+ reg_val &= ~(0x7 << bit_off);
+ reg_val |= (value & 0x7) << bit_off;
+ SDE_REG_WRITE(c, reg_off, reg_val);
+}
+
static void sde_hw_set_limit_conf(struct sde_hw_vbif *vbif,
u32 xin_id, bool rd, u32 limit)
{
@@ -144,6 +176,7 @@
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;
+ ops->set_mem_type = sde_hw_set_mem_type;
}
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 c67738b..80a9e5a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
@@ -71,6 +71,15 @@
*/
void (*set_qos_remap)(struct sde_hw_vbif *vbif,
u32 xin_id, u32 level, u32 remap_level);
+
+ /**
+ * set_mem_type - set memory type
+ * @vbif: vbif context driver
+ * @xin_id: client interface identifier
+ * @value: memory type value
+ */
+ void (*set_mem_type)(struct sde_hw_vbif *vbif,
+ u32 xin_id, u32 value);
};
struct sde_hw_vbif {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index 5dbd794..378b904 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -49,7 +49,7 @@
#define WB_UBWC_STATIC_CTRL 0x144
#define WB_CSC_BASE 0x260
#define WB_DST_ADDR_SW_STATUS 0x2B0
-#define WB_CDP_CTRL 0x2B4
+#define WB_CDP_CNTL 0x2B4
#define WB_OUT_IMAGE_SIZE 0x2C0
#define WB_OUT_XY 0x2C4
@@ -96,7 +96,6 @@
u32 write_config = 0;
u32 opmode = 0;
u32 dst_addr_sw = 0;
- u32 cdp_settings = 0x0;
chroma_samp = fmt->chroma_sample;
@@ -165,18 +164,6 @@
SDE_REG_WRITE(c, WB_OUT_SIZE, outsize);
SDE_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
SDE_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
-
- /* Enable CDP */
- cdp_settings = BIT(0);
-
- if (!SDE_FORMAT_IS_LINEAR(fmt))
- cdp_settings |= BIT(1);
-
- /* Enable 64 transactions if line mode*/
- if (data->intf_mode == INTF_MODE_WB_LINE)
- cdp_settings |= BIT(3);
-
- SDE_REG_WRITE(c, WB_CDP_CTRL, cdp_settings);
}
static void sde_hw_wb_roi(struct sde_hw_wb *ctx, struct sde_hw_wb_cfg *wb)
@@ -234,6 +221,27 @@
SDE_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
}
+static void sde_hw_wb_setup_cdp(struct sde_hw_wb *ctx,
+ struct sde_hw_wb_cdp_cfg *cfg)
+{
+ struct sde_hw_blk_reg_map *c;
+ u32 cdp_cntl = 0;
+
+ if (!ctx || !cfg)
+ return;
+
+ c = &ctx->hw;
+
+ if (cfg->enable)
+ cdp_cntl |= BIT(0);
+ if (cfg->ubwc_meta_enable)
+ cdp_cntl |= BIT(1);
+ if (cfg->preload_ahead == SDE_WB_CDP_PRELOAD_AHEAD_64)
+ cdp_cntl |= BIT(3);
+
+ SDE_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
+}
+
static void _setup_wb_ops(struct sde_hw_wb_ops *ops,
unsigned long features)
{
@@ -249,6 +257,9 @@
ops->setup_creq_lut = sde_hw_wb_setup_creq_lut;
ops->setup_qos_ctrl = sde_hw_wb_setup_qos_ctrl;
}
+
+ if (test_bit(SDE_WB_CDP, &features))
+ ops->setup_cdp = sde_hw_wb_setup_cdp;
}
struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.h b/drivers/gpu/drm/msm/sde/sde_hw_wb.h
index caf574e..ca3c386 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.h
@@ -29,6 +29,30 @@
};
/**
+ * enum CDP preload ahead address size
+ */
+enum {
+ SDE_WB_CDP_PRELOAD_AHEAD_32,
+ SDE_WB_CDP_PRELOAD_AHEAD_64
+};
+
+/**
+ * struct sde_hw_wb_cdp_cfg : CDP configuration
+ * @enable: true to enable CDP
+ * @ubwc_meta_enable: true to enable ubwc metadata preload
+ * @tile_amortize_enable: true to enable amortization control for tile format
+ * @preload_ahead: number of request to preload ahead
+ * SDE_WB_CDP_PRELOAD_AHEAD_32,
+ * SDE_WB_CDP_PRELOAD_AHEAD_64
+ */
+struct sde_hw_wb_cdp_cfg {
+ bool enable;
+ bool ubwc_meta_enable;
+ bool tile_amortize_enable;
+ u32 preload_ahead;
+};
+
+/**
* struct sde_hw_wb_qos_cfg : Writeback pipe QoS configuration
* @danger_lut: LUT for generate danger level based on fill level
* @safe_lut: LUT for generate safe level based on fill level
@@ -95,6 +119,14 @@
*/
void (*setup_qos_ctrl)(struct sde_hw_wb *ctx,
struct sde_hw_wb_qos_cfg *cfg);
+
+ /**
+ * setup_cdp - setup CDP
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to pipe CDP configuration
+ */
+ void (*setup_cdp)(struct sde_hw_wb *ctx,
+ struct sde_hw_wb_cdp_cfg *cfg);
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index a7d6ecf..c783ab0 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1238,6 +1238,10 @@
sde_hw_intr_destroy(sde_kms->hw_intr);
sde_kms->hw_intr = NULL;
+ if (sde_kms->power_event)
+ sde_power_handle_unregister_event(
+ &priv->phandle, sde_kms->power_event);
+
_sde_kms_release_displays(sde_kms);
/* safe to call these more than once during shutdown */
@@ -1443,6 +1447,16 @@
return ptr;
}
+static void sde_kms_handle_power_event(u32 event_type, void *usr)
+{
+ struct sde_kms *sde_kms = usr;
+
+ if (!sde_kms)
+ return;
+
+ if (event_type == SDE_POWER_EVENT_POST_ENABLE)
+ sde_vbif_init_memtypes(sde_kms);
+}
static int sde_kms_hw_init(struct msm_kms *kms)
{
@@ -1660,6 +1674,14 @@
*/
dev->mode_config.allow_fb_modifiers = true;
+ /*
+ * Handle (re)initializations during power enable
+ */
+ sde_kms_handle_power_event(SDE_POWER_EVENT_POST_ENABLE, sde_kms);
+ sde_kms->power_event = sde_power_handle_register_event(&priv->phandle,
+ SDE_POWER_EVENT_POST_ENABLE,
+ sde_kms_handle_power_event, sde_kms, "kms");
+
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
return 0;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index d20af9f..f73cb21 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -159,6 +159,7 @@
struct sde_power_client *core_client;
struct ion_client *iclient;
+ struct sde_power_event *power_event;
/* directory entry for debugfs */
struct dentry *debugfs_danger;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 88b0543..8941a54 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -3090,6 +3090,23 @@
psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags,
pstate->multirect_index);
+ if (psde->pipe_hw->ops.setup_cdp) {
+ struct sde_hw_pipe_cdp_cfg *cdp_cfg = &pstate->cdp_cfg;
+
+ memset(cdp_cfg, 0, sizeof(struct sde_hw_pipe_cdp_cfg));
+
+ cdp_cfg->enable = psde->catalog->perf.cdp_cfg
+ [SDE_PERF_CDP_USAGE_RT].rd_enable;
+ cdp_cfg->ubwc_meta_enable =
+ SDE_FORMAT_IS_UBWC(fmt);
+ cdp_cfg->tile_amortize_enable =
+ SDE_FORMAT_IS_UBWC(fmt) ||
+ SDE_FORMAT_IS_TILE(fmt);
+ cdp_cfg->preload_ahead = SDE_WB_CDP_PRELOAD_AHEAD_64;
+
+ psde->pipe_hw->ops.setup_cdp(psde->pipe_hw, cdp_cfg);
+ }
+
if (psde->pipe_hw->ops.setup_sys_cache) {
if (rstate->out_sbuf) {
if (rstate->nplane < 2)
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index 47611d1..92c077e 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -110,6 +110,7 @@
* @multirect_index: index of the rectangle of SSPP
* @multirect_mode: parallel or time multiplex multirect mode
* @pending: whether the current update is still pending
+ * @cdp_cfg: CDP configuration
*/
struct sde_plane_state {
struct drm_plane_state base;
@@ -126,6 +127,8 @@
/* @sc_cfg: system_cache configuration */
struct sde_hw_pipe_sc_cfg sc_cfg;
struct sde_plane_rot_state rot;
+
+ struct sde_hw_pipe_cdp_cfg cdp_cfg;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index c675216..e63fe8c 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -265,6 +265,26 @@
mdp->ops.setup_clk_force_ctrl(mdp, params->clk_ctrl, false);
}
+void sde_vbif_init_memtypes(struct sde_kms *sde_kms)
+{
+ struct sde_hw_vbif *vbif;
+ int i, j;
+
+ if (!sde_kms) {
+ SDE_ERROR("invalid argument\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
+ vbif = sde_kms->hw_vbif[i];
+ if (vbif && vbif->cap && vbif->ops.set_mem_type) {
+ for (j = 0; j < vbif->cap->memtype_count; j++)
+ vbif->ops.set_mem_type(
+ vbif, j, vbif->cap->memtype[j]);
+ }
+ }
+}
+
#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 d05c2e0..f1da68b1 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -27,6 +27,13 @@
u32 clk_ctrl;
};
+struct sde_vbif_set_memtype_params {
+ u32 xin_id;
+ u32 vbif_idx;
+ u32 clk_ctrl;
+ bool is_cacheable;
+};
+
/**
* struct sde_vbif_set_qos_params - QoS remapper parameter
* @vbif_idx: vbif identifier
@@ -59,6 +66,12 @@
void sde_vbif_set_qos_remap(struct sde_kms *sde_kms,
struct sde_vbif_set_qos_params *params);
+/**
+ * sde_vbif_init_memtypes - initialize xin memory types for vbif
+ * @sde_kms: SDE handler
+ */
+void sde_vbif_init_memtypes(struct sde_kms *sde_kms);
+
#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/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 6725c21..cb9726e 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -166,6 +166,7 @@
#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
#define SMR_VALID (1 << 31)
#define SMR_MASK_SHIFT 16
+#define SMR_MASK_MASK 0x7FFF
#define SMR_ID_SHIFT 0
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
@@ -335,10 +336,12 @@
enum arm_smmu_s2cr_type type;
enum arm_smmu_s2cr_privcfg privcfg;
u8 cbndx;
+ bool cb_handoff;
};
#define s2cr_init_val (struct arm_smmu_s2cr){ \
.type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS, \
+ .cb_handoff = false, \
}
struct arm_smmu_smr {
@@ -409,7 +412,6 @@
#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
#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;
@@ -528,7 +530,6 @@
static struct arm_smmu_option_prop arm_smmu_options[] = {
{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
{ 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},
@@ -553,6 +554,10 @@
static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain);
+static int arm_smmu_alloc_cb(struct iommu_domain *domain,
+ struct arm_smmu_device *smmu,
+ struct device *dev);
+
static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
{
return container_of(dom, struct arm_smmu_domain, domain);
@@ -1615,14 +1620,11 @@
if (is_iommu_pt_coherent(smmu_domain))
quirks |= IO_PGTABLE_QUIRK_PAGE_TABLE_COHERENT;
- /* Dynamic domains must set cbndx through domain attribute */
- if (!dynamic) {
- ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
- smmu->num_context_banks);
- if (ret < 0)
- goto out_unlock;
- cfg->cbndx = ret;
- }
+ ret = arm_smmu_alloc_cb(domain, smmu, dev);
+ if (ret < 0)
+ goto out_unlock;
+ cfg->cbndx = ret;
+
if (smmu->version < ARM_SMMU_V2) {
cfg->irptndx = atomic_inc_return(&smmu->irptndx);
cfg->irptndx %= smmu->num_context_irqs;
@@ -2243,14 +2245,18 @@
return ret;
}
+#define MAX_MAP_SG_BATCH_SIZE (SZ_4M)
static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents, int prot)
{
int ret;
- size_t size;
+ size_t size, batch_size, size_to_unmap = 0;
unsigned long flags;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+ unsigned int idx_start, idx_end;
+ struct scatterlist *sg_start, *sg_end;
+ unsigned long __saved_iova_start;
if (!ops)
return -ENODEV;
@@ -2259,17 +2265,45 @@
if (ret)
return ret;
- spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
- ret = ops->map_sg(ops, iova, sg, nents, prot, &size);
- spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+ __saved_iova_start = iova;
+ idx_start = idx_end = 0;
+ sg_start = sg_end = sg;
+ while (idx_end < nents) {
+ batch_size = sg_end->length;
+ sg_end = sg_next(sg_end);
+ idx_end++;
+ while ((idx_end < nents) &&
+ (batch_size + sg_end->length < MAX_MAP_SG_BATCH_SIZE)) {
- if (!ret)
- arm_smmu_unmap(domain, iova, size);
+ batch_size += sg_end->length;
+ sg_end = sg_next(sg_end);
+ idx_end++;
+ }
- arm_smmu_domain_power_off(domain, smmu_domain->smmu);
+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+ ret = ops->map_sg(ops, iova, sg_start, idx_end - idx_start,
+ prot, &size);
+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+ /* Returns 0 on error */
+ if (!ret) {
+ size_to_unmap = iova + size - __saved_iova_start;
+ goto out;
+ }
+
+ iova += batch_size;
+ idx_start = idx_end;
+ sg_start = sg_end;
+ }
+
+out:
arm_smmu_assign_table(smmu_domain);
- return ret;
+ if (size_to_unmap) {
+ arm_smmu_unmap(domain, __saved_iova_start, size_to_unmap);
+ iova = __saved_iova_start;
+ }
+ arm_smmu_domain_power_off(domain, smmu_domain->smmu);
+ return iova - __saved_iova_start;
}
static phys_addr_t __arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
@@ -3187,12 +3221,10 @@
* Reset stream mapping groups: Initial values mark all SMRn as
* invalid and all S2CRn as bypass unless overridden.
*/
- if (!(smmu->options & ARM_SMMU_OPT_SKIP_INIT)) {
- for (i = 0; i < smmu->num_mapping_groups; ++i)
- arm_smmu_write_sme(smmu, i);
+ for (i = 0; i < smmu->num_mapping_groups; ++i)
+ arm_smmu_write_sme(smmu, i);
- arm_smmu_context_bank_reset(smmu);
- }
+ arm_smmu_context_bank_reset(smmu);
/* Invalidate the TLB, just in case */
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
@@ -3249,6 +3281,92 @@
}
}
+
+/*
+ * Some context banks needs to be transferred from bootloader to HLOS in a way
+ * that allows ongoing traffic. The current expectation is that these context
+ * banks operate in bypass mode.
+ * Additionally, there must be exactly one device in devicetree with stream-ids
+ * overlapping those used by the bootloader.
+ */
+static int arm_smmu_alloc_cb(struct iommu_domain *domain,
+ struct arm_smmu_device *smmu,
+ struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+ u32 i, idx;
+ int cb = -EINVAL;
+ bool dynamic;
+
+ /* Dynamic domains must set cbndx through domain attribute */
+ dynamic = is_dynamic_domain(domain);
+ if (dynamic)
+ return INVALID_CBNDX;
+
+ mutex_lock(&smmu->stream_map_mutex);
+ for_each_cfg_sme(fwspec, i, idx) {
+ if (smmu->s2crs[idx].cb_handoff)
+ cb = smmu->s2crs[idx].cbndx;
+ }
+
+ if (cb < 0) {
+ mutex_unlock(&smmu->stream_map_mutex);
+ return __arm_smmu_alloc_bitmap(smmu->context_map,
+ smmu->num_s2_context_banks,
+ smmu->num_context_banks);
+ }
+
+ for (i = 0; i < smmu->num_mapping_groups; i++) {
+ if (smmu->s2crs[i].cbndx == cb) {
+ smmu->s2crs[i].cbndx = 0;
+ smmu->s2crs[i].cb_handoff = false;
+ smmu->s2crs[i].count -= 1;
+ }
+ }
+ mutex_unlock(&smmu->stream_map_mutex);
+
+ return cb;
+}
+
+static int arm_smmu_handoff_cbs(struct arm_smmu_device *smmu)
+{
+ u32 i, raw_smr, raw_s2cr;
+ struct arm_smmu_smr smr;
+ struct arm_smmu_s2cr s2cr;
+
+ for (i = 0; i < smmu->num_mapping_groups; i++) {
+ raw_smr = readl_relaxed(ARM_SMMU_GR0(smmu) +
+ ARM_SMMU_GR0_SMR(i));
+ if (!(raw_smr & SMR_VALID))
+ continue;
+
+ smr.mask = (raw_smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
+ smr.id = (u16)raw_smr;
+ smr.valid = true;
+
+ raw_s2cr = readl_relaxed(ARM_SMMU_GR0(smmu) +
+ ARM_SMMU_GR0_S2CR(i));
+ s2cr.group = NULL;
+ s2cr.count = 1;
+ s2cr.type = (raw_s2cr >> S2CR_TYPE_SHIFT) & S2CR_TYPE_MASK;
+ s2cr.privcfg = (raw_s2cr >> S2CR_PRIVCFG_SHIFT) &
+ S2CR_PRIVCFG_MASK;
+ s2cr.cbndx = (u8)raw_s2cr;
+ s2cr.cb_handoff = true;
+
+ if (s2cr.type != S2CR_TYPE_TRANS)
+ continue;
+
+ smmu->smrs[i] = smr;
+ smmu->s2crs[i] = s2cr;
+ bitmap_set(smmu->context_map, s2cr.cbndx, 1);
+ dev_dbg(smmu->dev, "Handoff smr: %x s2cr: %x cb: %d\n",
+ raw_smr, raw_s2cr, s2cr.cbndx);
+ }
+
+ return 0;
+}
+
static int arm_smmu_parse_impl_def_registers(struct arm_smmu_device *smmu)
{
struct device *dev = smmu->dev;
@@ -3508,6 +3626,7 @@
smmu->streamid_mask = size - 1;
if (id & ID0_SMS) {
u32 smr;
+ int i;
smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK;
@@ -3522,14 +3641,25 @@
* bits are set, so check each one separately. We can reject
* masters later if they try to claim IDs outside these masks.
*/
+ for (i = 0; i < size; i++) {
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(i));
+ if (!(smr & SMR_VALID))
+ break;
+ }
+ if (i == size) {
+ dev_err(smmu->dev,
+ "Unable to compute streamid_masks\n");
+ return -ENODEV;
+ }
+
smr = smmu->streamid_mask << SMR_ID_SHIFT;
- writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
- smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+ writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(i));
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(i));
smmu->streamid_mask = smr >> SMR_ID_SHIFT;
smr = smmu->streamid_mask << SMR_MASK_SHIFT;
- writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
- smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+ writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(i));
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(i));
smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
/* Zero-initialised to mark as invalid */
@@ -3821,6 +3951,10 @@
if (err)
goto out_power_off;
+ err = arm_smmu_handoff_cbs(smmu);
+ if (err)
+ goto out_power_off;
+
err = arm_smmu_parse_impl_def_registers(smmu);
if (err)
goto out_power_off;
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index ac3059d..560bb43 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -765,6 +765,51 @@
return ERR_PTR(-ENOMEM);
}
+/*
+ * Based off of similar code from dma-iommu.c, but modified to use a different
+ * iova allocator
+ */
+static void fast_smmu_reserve_pci_windows(struct device *dev,
+ struct dma_fast_smmu_mapping *mapping)
+{
+ struct pci_host_bridge *bridge;
+ struct resource_entry *window;
+ phys_addr_t start, end;
+ struct pci_dev *pci_dev;
+ unsigned long flags;
+
+ if (!dev_is_pci(dev))
+ return;
+
+ pci_dev = to_pci_dev(dev);
+ bridge = pci_find_host_bridge(pci_dev->bus);
+
+ spin_lock_irqsave(&mapping->lock, flags);
+ resource_list_for_each_entry(window, &bridge->windows) {
+ if (resource_type(window->res) != IORESOURCE_MEM &&
+ resource_type(window->res) != IORESOURCE_IO)
+ continue;
+
+ start = round_down(window->res->start - window->offset,
+ FAST_PAGE_SIZE);
+ end = round_up(window->res->end - window->offset,
+ FAST_PAGE_SIZE);
+ start = max_t(unsigned long, mapping->base, start);
+ end = min_t(unsigned long, mapping->base + mapping->size, end);
+ if (start >= end)
+ continue;
+
+ dev_dbg(dev, "iova allocator reserved 0x%pa-0x%pa\n",
+ &start, &end);
+
+ start = (start - mapping->base) >> FAST_PAGE_SHIFT;
+ end = (end - mapping->base) >> FAST_PAGE_SHIFT;
+ bitmap_set(mapping->bitmap, start, end - start);
+ }
+ spin_unlock_irqrestore(&mapping->lock, flags);
+}
+
+
/**
* fast_smmu_attach_device
* @dev: valid struct device pointer
@@ -798,6 +843,8 @@
mapping->fast->domain = domain;
mapping->fast->dev = dev;
+ fast_smmu_reserve_pci_windows(dev, mapping->fast);
+
group = dev->iommu_group;
if (!group) {
dev_err(dev, "No iommu associated with device\n");
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 048ae92..6bb435b 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1712,6 +1712,7 @@
* 0: normal mapping
* 1: force coherent mapping
* 2: force non-cohernet mapping
+ * 3: use system cache
*/
static ssize_t iommu_debug_dma_map_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *offset)
@@ -1782,6 +1783,8 @@
dma_attrs = DMA_ATTR_FORCE_COHERENT;
else if (attr == 2)
dma_attrs = DMA_ATTR_FORCE_NON_COHERENT;
+ else if (attr == 3)
+ dma_attrs = DMA_ATTR_IOMMU_USE_UPSTREAM_HINT;
else
goto invalid_format;
@@ -1803,7 +1806,7 @@
return retval;
invalid_format:
- pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n");
+ pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n3: use system cache\n");
return retval;
invalid_addr:
@@ -1984,6 +1987,8 @@
dma_attrs = DMA_ATTR_FORCE_COHERENT;
else if (attr == 2)
dma_attrs = DMA_ATTR_FORCE_NON_COHERENT;
+ else if (attr == 3)
+ dma_attrs = DMA_ATTR_IOMMU_USE_UPSTREAM_HINT;
else
goto invalid_format;
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 7d8d002..b0b51d6 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -40,9 +40,6 @@
#define SMMU_SIZE ((SZ_1G * 4ULL) - SMMU_BASE)
#define WIGIG_ENABLE_DELAY 50
-#define PM_OPT_SUSPEND (MSM_PCIE_CONFIG_NO_CFG_RESTORE | \
- MSM_PCIE_CONFIG_LINKDOWN)
-#define PM_OPT_RESUME MSM_PCIE_CONFIG_NO_CFG_RESTORE
#define WIGIG_SUBSYS_NAME "WIGIG"
#define WIGIG_RAMDUMP_SIZE 0x200000 /* maximum ramdump size */
@@ -127,6 +124,8 @@
bool use_cpu_boost;
bool is_cpu_boosted;
struct cpumask boost_cpu;
+
+ bool keep_radio_on_during_sleep;
};
static LIST_HEAD(dev_list);
@@ -523,30 +522,8 @@
return rc;
}
-static int ops_suspend(void *handle, bool keep_device_power)
+static int msm_11ad_turn_device_power_off(struct msm11ad_ctx *ctx)
{
- int rc;
- struct msm11ad_ctx *ctx = handle;
- struct pci_dev *pcidev;
-
- pr_info("%s(%p)\n", __func__, handle);
- if (!ctx) {
- pr_err("No context\n");
- return -ENODEV;
- }
- pcidev = ctx->pcidev;
- rc = pci_save_state(pcidev);
- if (rc) {
- dev_err(ctx->dev, "pci_save_state failed :%d\n", rc);
- return rc;
- }
- rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
- pcidev, NULL, PM_OPT_SUSPEND);
- if (rc) {
- dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n",
- rc);
- return rc;
- }
if (ctx->gpio_en >= 0)
gpio_direction_output(ctx->gpio_en, 0);
@@ -557,20 +534,12 @@
msm_11ad_disable_vregs(ctx);
- return rc;
+ return 0;
}
-static int ops_resume(void *handle, bool device_powered_on)
+static int msm_11ad_turn_device_power_on(struct msm11ad_ctx *ctx)
{
int rc;
- struct msm11ad_ctx *ctx = handle;
- struct pci_dev *pcidev;
-
- pr_info("%s(%p)\n", __func__, handle);
- if (!ctx) {
- pr_err("No context\n");
- return -ENODEV;
- }
rc = msm_11ad_enable_vregs(ctx);
if (rc) {
@@ -588,25 +557,124 @@
if (ctx->sleep_clk_en >= 0)
gpio_direction_output(ctx->sleep_clk_en, 1);
- pcidev = ctx->pcidev;
if (ctx->gpio_en >= 0) {
gpio_direction_output(ctx->gpio_en, 1);
msleep(WIGIG_ENABLE_DELAY);
}
+ return 0;
+
+err_disable_vregs:
+ msm_11ad_disable_vregs(ctx);
+ return rc;
+}
+
+static int msm_11ad_suspend_power_off(void *handle)
+{
+ int rc;
+ struct msm11ad_ctx *ctx = handle;
+ struct pci_dev *pcidev;
+
+ pr_debug("%s\n", __func__);
+
+ if (!ctx) {
+ pr_err("%s: No context\n", __func__);
+ return -ENODEV;
+ }
+
+ pcidev = ctx->pcidev;
+
+ msm_pcie_shadow_control(ctx->pcidev, 0);
+
+ rc = pci_save_state(pcidev);
+ if (rc) {
+ dev_err(ctx->dev, "pci_save_state failed :%d\n", rc);
+ goto out;
+ }
+ ctx->pristine_state = pci_store_saved_state(pcidev);
+
+ rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
+ pcidev, NULL, 0);
+ if (rc) {
+ dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n",
+ rc);
+ goto out;
+ }
+
+ rc = msm_11ad_turn_device_power_off(ctx);
+
+out:
+ return rc;
+}
+
+static int ops_suspend(void *handle, bool keep_device_power)
+{
+ struct msm11ad_ctx *ctx = handle;
+ struct pci_dev *pcidev;
+ int rc;
+
+ pr_debug("11ad suspend: %s\n", __func__);
+ if (!ctx) {
+ pr_err("11ad suspend: No context\n");
+ return -ENODEV;
+ }
+
+ if (!keep_device_power)
+ return msm_11ad_suspend_power_off(handle);
+
+ pcidev = ctx->pcidev;
+
+ msm_pcie_shadow_control(pcidev, 0);
+
+ dev_dbg(ctx->dev, "disable device and save config\n");
+ pci_disable_device(pcidev);
+ pci_save_state(pcidev);
+ ctx->pristine_state = pci_store_saved_state(pcidev);
+ dev_dbg(ctx->dev, "moving to D3\n");
+ pci_set_power_state(pcidev, PCI_D3hot);
+
+ rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
+ pcidev, NULL, 0);
+ if (rc)
+ dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n",
+ rc);
+
+ return rc;
+}
+
+static int msm_11ad_resume_power_on(void *handle)
+{
+ int rc;
+ struct msm11ad_ctx *ctx = handle;
+ struct pci_dev *pcidev;
+
+ pr_debug("%s\n", __func__);
+
+ if (!ctx) {
+ pr_err("%s: No context\n", __func__);
+ return -ENODEV;
+ }
+ pcidev = ctx->pcidev;
+
+ rc = msm_11ad_turn_device_power_on(ctx);
+ if (rc)
+ return rc;
+
rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
- pcidev, NULL, PM_OPT_RESUME);
+ pcidev, NULL, 0);
if (rc) {
dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n",
rc);
goto err_disable_power;
}
- rc = msm_pcie_recover_config(pcidev);
- if (rc) {
- dev_err(ctx->dev, "msm_pcie_recover_config failed :%d\n",
- rc);
- goto err_suspend_rc;
- }
+
+ pci_set_power_state(pcidev, PCI_D0);
+
+ if (ctx->pristine_state)
+ pci_load_saved_state(ctx->pcidev, ctx->pristine_state);
+ pci_restore_state(ctx->pcidev);
+
+ msm_pcie_shadow_control(ctx->pcidev, 1);
/* Disable L1, in case it is enabled */
if (ctx->l1_enabled_in_enum) {
@@ -622,18 +690,54 @@
err_suspend_rc:
msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
- pcidev, NULL, PM_OPT_SUSPEND);
+ pcidev, NULL, 0);
err_disable_power:
- if (ctx->gpio_en >= 0)
- gpio_direction_output(ctx->gpio_en, 0);
+ msm_11ad_turn_device_power_off(ctx);
+ return rc;
+}
- if (ctx->sleep_clk_en >= 0)
- gpio_direction_output(ctx->sleep_clk_en, 0);
+static int ops_resume(void *handle, bool device_powered_on)
+{
+ struct msm11ad_ctx *ctx = handle;
+ struct pci_dev *pcidev;
+ int rc;
- msm_11ad_disable_clocks(ctx);
-err_disable_vregs:
- msm_11ad_disable_vregs(ctx);
+ pr_debug("11ad resume: %s\n", __func__);
+ if (!ctx) {
+ pr_err("11ad resume: No context\n");
+ return -ENODEV;
+ }
+ pcidev = ctx->pcidev;
+
+ if (!device_powered_on)
+ return msm_11ad_resume_power_on(handle);
+
+ rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
+ pcidev, NULL, 0);
+ if (rc) {
+ dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n",
+ rc);
+ return rc;
+ }
+ pci_set_power_state(pcidev, PCI_D0);
+
+ dev_dbg(ctx->dev, "restore state and enable device\n");
+ pci_load_saved_state(pcidev, ctx->pristine_state);
+ pci_restore_state(pcidev);
+
+ rc = pci_enable_device(pcidev);
+ if (rc) {
+ dev_err(ctx->dev, "pci_enable_device failed (%d)\n", rc);
+ goto out;
+ }
+
+ msm_pcie_shadow_control(pcidev, 1);
+
+ dev_dbg(ctx->dev, "pci set master\n");
+ pci_set_master(pcidev);
+
+out:
return rc;
}
@@ -992,6 +1096,8 @@
return -EINVAL;
}
ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support");
+ ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node,
+ "qcom,keep_radio_on_during_sleep");
ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
@@ -1104,13 +1210,6 @@
}
}
- rc = pci_save_state(pcidev);
- if (rc) {
- dev_err(ctx->dev, "pci_save_state failed :%d\n", rc);
- goto out_rc;
- }
- ctx->pristine_state = pci_store_saved_state(pcidev);
-
if (ctx->sleep_clk_en >= 0) {
rc = gpio_request(ctx->sleep_clk_en, "msm_11ad");
if (rc < 0) {
@@ -1146,7 +1245,7 @@
device_disable_async_suspend(&pcidev->dev);
list_add_tail(&ctx->list, &dev_list);
- ops_suspend(ctx, false);
+ msm_11ad_suspend_power_off(ctx);
return 0;
out_rc:
@@ -1236,6 +1335,17 @@
dev_warn(ctx->dev, "failed to set CPU boost affinity\n");
}
+static void msm_11ad_clear_boost_affinity(struct msm11ad_ctx *ctx)
+{
+ int rc;
+
+ irq_modify_status(ctx->pcidev->irq, IRQ_NO_BALANCING, 0);
+ rc = irq_set_affinity_hint(ctx->pcidev->irq, NULL);
+ if (rc)
+ dev_warn(ctx->dev,
+ "Failed clear affinity, rc=%d\n", rc);
+}
+
/* hooks for the wil6210 driver */
static int ops_bus_request(void *handle, u32 kbps /* KBytes/Sec */)
{
@@ -1287,8 +1397,7 @@
dev_err(ctx->dev,
"Failed disable boost rc=%d\n",
rc);
- irq_modify_status(ctx->pcidev->irq,
- IRQ_NO_BALANCING, 0);
+ msm_11ad_clear_boost_affinity(ctx);
dev_dbg(ctx->dev, "CPU boost disabled\n");
}
ctx->is_cpu_boosted = needs_boost;
@@ -1316,7 +1425,7 @@
memset(&ctx->rops, 0, sizeof(ctx->rops));
ctx->wil_handle = NULL;
- ops_suspend(ctx, false);
+ msm_11ad_suspend_power_off(ctx);
}
static int msm_11ad_notify_crash(struct msm11ad_ctx *ctx)
@@ -1374,6 +1483,16 @@
return rc;
}
+static bool ops_keep_radio_on_during_sleep(void *handle)
+{
+ struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
+
+ pr_debug("%s: keep radio on during sleep is %s\n", __func__,
+ ctx->keep_radio_on_during_sleep ? "allowed" : "not allowed");
+
+ return ctx->keep_radio_on_during_sleep;
+}
+
void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops,
const struct wil_platform_rops *rops, void *wil_handle)
{
@@ -1413,6 +1532,7 @@
ops->resume = ops_resume;
ops->uninit = ops_uninit;
ops->notify = ops_notify;
+ ops->keep_radio_on_during_sleep = ops_keep_radio_on_during_sleep;
return ctx;
}
@@ -1429,19 +1549,9 @@
return -EINVAL;
}
- if (ctx->pristine_state) {
- /* in old kernels, pci_load_saved_state() is not exported;
- * so use pci_load_and_free_saved_state()
- * and re-allocate ctx->saved_state again
- */
- pci_load_and_free_saved_state(ctx->pcidev,
- &ctx->pristine_state);
- ctx->pristine_state = pci_store_saved_state(ctx->pcidev);
- }
-
ctx->subsys_handle = subsystem_get(ctx->subsysdesc.name);
- return ops_resume(ctx, false);
+ return msm_11ad_resume_power_on(ctx);
}
EXPORT_SYMBOL(msm_11ad_modinit);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index c2ac982..967bb0d 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2792,10 +2792,10 @@
if (sdkp->opt_xfer_blocks &&
sdkp->opt_xfer_blocks <= dev_max &&
sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
- logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_SIZE) {
- q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
- rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
- } else
+ sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_SIZE)
+ rw_max = q->limits.io_opt =
+ sdkp->opt_xfer_blocks * sdp->sector_size;
+ else
rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
(sector_t)BLK_DEF_MAX_SECTORS);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index c8d9863..4446ed2 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -151,11 +151,6 @@
return blocks << (ilog2(sdev->sector_size) - 9);
}
-static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t blocks)
-{
- return blocks * sdev->sector_size;
-}
-
/*
* Look up the DIX operation based on whether the command is read or
* write and whether dix and dif are enabled.
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6418c11..77ba414 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -381,6 +381,8 @@
bool is_gating_context);
static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba,
bool is_gating_context);
+static void ufshcd_hold_all(struct ufs_hba *hba);
+static void ufshcd_release_all(struct ufs_hba *hba);
static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static inline void ufshcd_save_tstamp_of_last_dme_cmd(struct ufs_hba *hba);
@@ -2055,6 +2057,22 @@
return;
}
+static void __ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba,
+ unsigned long delay_ms)
+{
+ pm_runtime_get_sync(hba->dev);
+ ufshcd_hold_all(hba);
+ ufshcd_scsi_block_requests(hba);
+ down_write(&hba->lock);
+ /* wait for all the outstanding requests to finish */
+ ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
+ ufshcd_set_auto_hibern8_timer(hba, delay_ms);
+ up_write(&hba->lock);
+ ufshcd_scsi_unblock_requests(hba);
+ ufshcd_release_all(hba);
+ pm_runtime_put_sync(hba->dev);
+}
+
static void ufshcd_hibern8_exit_work(struct work_struct *work)
{
int ret;
@@ -2106,19 +2124,32 @@
{
struct ufs_hba *hba = dev_get_drvdata(dev);
unsigned long flags, value;
+ bool change = true;
if (kstrtoul(buf, 0, &value))
return -EINVAL;
spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->hibern8_on_idle.delay_ms == value)
+ change = false;
+
+ if (value >= hba->clk_gating.delay_ms_pwr_save ||
+ value >= hba->clk_gating.delay_ms_perf) {
+ dev_err(hba->dev, "hibern8_on_idle_delay (%lu) can not be >= to clkgate_delay_ms_pwr_save (%lu) and clkgate_delay_ms_perf (%lu)\n",
+ value, hba->clk_gating.delay_ms_pwr_save,
+ hba->clk_gating.delay_ms_perf);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return -EINVAL;
+ }
+
hba->hibern8_on_idle.delay_ms = value;
spin_unlock_irqrestore(hba->host->host_lock, flags);
/* Update auto hibern8 timer value if supported */
- if (ufshcd_is_auto_hibern8_supported(hba) &&
+ if (change && ufshcd_is_auto_hibern8_supported(hba) &&
hba->hibern8_on_idle.is_enabled)
- ufshcd_set_auto_hibern8_timer(hba,
- hba->hibern8_on_idle.delay_ms);
+ __ufshcd_set_auto_hibern8_timer(hba,
+ hba->hibern8_on_idle.delay_ms);
return count;
}
@@ -2148,7 +2179,7 @@
/* Update auto hibern8 timer value if supported */
if (ufshcd_is_auto_hibern8_supported(hba)) {
- ufshcd_set_auto_hibern8_timer(hba,
+ __ufshcd_set_auto_hibern8_timer(hba,
value ? hba->hibern8_on_idle.delay_ms : value);
goto update;
}
diff --git a/include/linux/sched.h b/include/linux/sched.h
index decb943..6c6ae4d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -178,7 +178,9 @@
#endif
extern void sched_update_nr_prod(int cpu, long delta, bool inc);
-extern void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg);
+extern void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg,
+ unsigned int *max_nr,
+ unsigned int *big_max_nr);
extern unsigned int sched_get_cpu_util(int cpu);
extern void calc_global_load(unsigned long ticks);
diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h
new file mode 100644
index 0000000..f2322f3
--- /dev/null
+++ b/include/linux/usb/audio-v3.h
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices in version 3.0.
+ * Comments below reference relevant sections of the documents contained
+ * in http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip
+ */
+
+#ifndef __LINUX_USB_AUDIO_V3_H
+#define __LINUX_USB_AUDIO_V3_H
+
+#include <linux/types.h>
+
+#define UAC3_MIXER_UNIT_V3 0x05
+#define UAC3_FEATURE_UNIT_V3 0x07
+#define UAC3_CLOCK_SOURCE 0x0b
+
+#define BADD_MAXPSIZE_SYNC_MONO_16 0x0060
+#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090
+#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0
+#define BADD_MAXPSIZE_SYNC_STEREO_24 0x0120
+
+#define BADD_MAXPSIZE_ASYNC_MONO_16 0x0062
+#define BADD_MAXPSIZE_ASYNC_MONO_24 0x0093
+#define BADD_MAXPSIZE_ASYNC_STEREO_16 0x00c4
+#define BADD_MAXPSIZE_ASYNC_STEREO_24 0x0126
+
+#define BIT_RES_16_BIT 0x10
+#define BIT_RES_24_BIT 0x18
+
+#define SUBSLOTSIZE_16_BIT 0x02
+#define SUBSLOTSIZE_24_BIT 0x03
+
+#define BADD_SAMPLING_RATE 48000
+
+#define NUM_CHANNELS_MONO 1
+#define NUM_CHANNELS_STEREO 2
+#define BADD_CH_CONFIG_MONO 0
+#define BADD_CH_CONFIG_STEREO 3
+#define CLUSTER_ID_MONO 0x0001
+#define CLUSTER_ID_STEREO 0x0002
+
+#define FULL_ADC_PROFILE 0x01
+
+/* BADD Profile IDs */
+#define PROF_GENERIC_IO 0x20
+#define PROF_HEADPHONE 0x21
+#define PROF_SPEAKER 0x22
+#define PROF_MICROPHONE 0x23
+#define PROF_HEADSET 0x24
+#define PROF_HEADSET_ADAPTER 0x25
+#define PROF_SPEAKERPHONE 0x26
+
+/* BADD Entity IDs */
+#define BADD_OUT_TERM_ID_BAOF 0x03
+#define BADD_OUT_TERM_ID_BAIF 0x06
+#define BADD_IN_TERM_ID_BAOF 0x01
+#define BADD_IN_TERM_ID_BAIF 0x04
+#define BADD_FU_ID_BAOF 0x02
+#define BADD_FU_ID_BAIF 0x05
+#define BADD_CLOCK_SOURCE 0x09
+#define BADD_FU_ID_BAIOF 0x07
+#define BADD_MU_ID_BAIOF 0x08
+
+#define UAC_BIDIR_TERMINAL_HEADSET 0x0402
+#define UAC_BIDIR_TERMINAL_SPEAKERPHONE 0x0403
+
+#define NUM_BADD_DESCS 7
+
+struct uac3_input_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __u16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bCSourceID;
+ __u32 bmControls;
+ __u16 wClusterDescrID;
+ __u16 wExTerminalDescrID;
+ __u16 wConnectorsDescrID;
+ __u16 wTerminalDescrStr;
+} __packed;
+
+#define UAC3_DT_INPUT_TERMINAL_SIZE 0x14
+
+extern struct uac3_input_terminal_descriptor badd_baif_in_term_desc;
+extern struct uac3_input_terminal_descriptor badd_baof_in_term_desc;
+
+struct uac3_output_terminal_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bTerminalID;
+ __u16 wTerminalType;
+ __u8 bAssocTerminal;
+ __u8 bSourceID;
+ __u8 bCSourceID;
+ __u32 bmControls;
+ __u16 wExTerminalDescrID;
+ __u16 wConnectorsDescrID;
+ __u16 wTerminalDescrStr;
+} __packed;
+
+#define UAC3_DT_OUTPUT_TERMINAL_SIZE 0x13
+
+extern struct uac3_output_terminal_descriptor badd_baif_out_term_desc;
+extern struct uac3_output_terminal_descriptor badd_baof_out_term_desc;
+
+extern __u8 monoControls[];
+extern __u8 stereoControls[];
+extern __u8 badd_mu_src_ids[];
+
+struct uac3_mixer_unit_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bUnitID;
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+ __u16 wClusterDescrID;
+ __u8 bmMixerControls;
+ __u32 bmControls;
+ __u16 wMixerDescrStr;
+} __packed;
+
+#define UAC3_DT_MIXER_UNIT_SIZE 0x10
+
+extern struct uac3_mixer_unit_descriptor badd_baiof_mu_desc;
+
+struct uac3_feature_unit_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bUnitID;
+ __u8 bSourceID;
+ __u8 *bmaControls;
+ __u16 wFeatureDescrStr;
+} __packed;
+
+extern struct uac3_feature_unit_descriptor badd_baif_fu_desc;
+extern struct uac3_feature_unit_descriptor badd_baof_fu_desc;
+extern struct uac3_feature_unit_descriptor badd_baiof_fu_desc;
+
+struct uac3_clock_source_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bDescriptorSubtype;
+ __u8 bClockID;
+ __u8 bmAttributes;
+ __u32 bmControls;
+ __u8 bReferenceTerminal;
+ __u16 wClockSourceStr;
+} __packed;
+
+#define UAC3_DT_CLOCK_SRC_SIZE 0x0c
+
+extern struct uac3_clock_source_descriptor badd_clock_desc;
+
+extern void *badd_desc_list[];
+
+#endif /* __LINUX_USB_AUDIO_V3_H */
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 4a9c625..8c1746a 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1834,24 +1834,30 @@
TRACE_EVENT(sched_get_nr_running_avg,
- TP_PROTO(int avg, int big_avg, int iowait_avg),
+ TP_PROTO(int avg, int big_avg, int iowait_avg,
+ unsigned int max_nr, unsigned int big_max_nr),
- TP_ARGS(avg, big_avg, iowait_avg),
+ TP_ARGS(avg, big_avg, iowait_avg, max_nr, big_max_nr),
TP_STRUCT__entry(
__field( int, avg )
__field( int, big_avg )
__field( int, iowait_avg )
+ __field( unsigned int, max_nr )
+ __field( unsigned int, big_max_nr )
),
TP_fast_assign(
__entry->avg = avg;
__entry->big_avg = big_avg;
__entry->iowait_avg = iowait_avg;
+ __entry->max_nr = max_nr;
+ __entry->big_max_nr = big_max_nr;
),
- TP_printk("avg=%d big_avg=%d iowait_avg=%d",
- __entry->avg, __entry->big_avg, __entry->iowait_avg)
+ TP_printk("avg=%d big_avg=%d iowait_avg=%d max_nr=%u big_max_nr=%u",
+ __entry->avg, __entry->big_avg, __entry->iowait_avg,
+ __entry->max_nr, __entry->big_max_nr)
);
TRACE_EVENT(core_ctl_eval_need,
diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h
index d2314be..c6f5b09 100644
--- a/include/uapi/linux/usb/audio.h
+++ b/include/uapi/linux/usb/audio.h
@@ -26,6 +26,7 @@
/* bInterfaceProtocol values to denote the version of the standard used */
#define UAC_VERSION_1 0x00
#define UAC_VERSION_2 0x20
+#define UAC_VERSION_3 0x30
/* A.2 Audio Interface Subclass Codes */
#define USB_SUBCLASS_AUDIOCONTROL 0x01
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index e594804..b140e55 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -39,6 +39,7 @@
cpumask_t cpu_mask;
unsigned int need_cpus;
unsigned int task_thres;
+ unsigned int max_nr;
s64 need_ts;
struct list_head lru;
bool pending;
@@ -458,47 +459,25 @@
/* ==================== runqueue based core count =================== */
-#define NR_RUNNING_TOLERANCE 5
-
static void update_running_avg(void)
{
int avg, iowait_avg, big_avg;
+ int max_nr, big_max_nr;
struct cluster_data *cluster;
unsigned int index = 0;
- sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg);
-
- /*
- * Round up to the next integer if the average nr running tasks
- * is within NR_RUNNING_TOLERANCE/100 of the next integer.
- * If normal rounding up is used, it will allow a transient task
- * to trigger online event. By the time core is onlined, the task
- * has finished.
- * Rounding to closest suffers same problem because scheduler
- * might only provide running stats per jiffy, and a transient
- * task could skew the number for one jiffy. If core control
- * samples every 2 jiffies, it will observe 0.5 additional running
- * average which rounds up to 1 task.
- */
- avg = (avg + NR_RUNNING_TOLERANCE) / 100;
- big_avg = (big_avg + NR_RUNNING_TOLERANCE) / 100;
+ sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg,
+ &max_nr, &big_max_nr);
for_each_cluster(cluster, index) {
if (!cluster->inited)
continue;
- /*
- * Big cluster only need to take care of big tasks, but if
- * there are not enough big cores, big tasks need to be run
- * on little as well. Thus for little's runqueue stat, it
- * has to use overall runqueue average, or derive what big
- * tasks would have to be run on little. The latter approach
- * is not easy to get given core control reacts much slower
- * than scheduler, and can't predict scheduler's behavior.
- */
cluster->nrrun = cluster->is_big_cluster ? big_avg : avg;
+ cluster->max_nr = cluster->is_big_cluster ? big_max_nr : max_nr;
}
}
+#define MAX_NR_THRESHOLD 4
/* adjust needed CPUs based on current runqueue information */
static unsigned int apply_task_need(const struct cluster_data *cluster,
unsigned int new_need)
@@ -509,7 +488,15 @@
/* only unisolate more cores if there are tasks to run */
if (cluster->nrrun > new_need)
- return new_need + 1;
+ new_need = new_need + 1;
+
+ /*
+ * We don't want tasks to be overcrowded in a cluster.
+ * If any CPU has more than MAX_NR_THRESHOLD in the last
+ * window, bring another CPU to help out.
+ */
+ if (cluster->max_nr > MAX_NR_THRESHOLD)
+ new_need = new_need + 1;
return new_need;
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 5220511..3194ae6 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2451,6 +2451,11 @@
return max_possible_capacity != min_max_possible_capacity;
}
+static inline bool is_max_capacity_cpu(int cpu)
+{
+ return cpu_max_possible_capacity(cpu) == max_possible_capacity;
+}
+
/*
* 'load' is in reference to "best cpu" at its best frequency.
* Scale that in reference to a given cpu, accounting for how bad it is
@@ -2719,6 +2724,8 @@
return 0;
}
+static inline bool is_max_capacity_cpu(int cpu) { return true; }
+
static inline void
inc_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra) { }
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index f820094..7f86c0b 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -27,11 +27,13 @@
static DEFINE_PER_CPU(u64, last_time);
static DEFINE_PER_CPU(u64, nr_big_prod_sum);
static DEFINE_PER_CPU(u64, nr);
+static DEFINE_PER_CPU(u64, nr_max);
static DEFINE_PER_CPU(unsigned long, iowait_prod_sum);
static DEFINE_PER_CPU(spinlock_t, nr_lock) = __SPIN_LOCK_UNLOCKED(nr_lock);
static s64 last_get_time;
+#define DIV64_U64_ROUNDUP(X, Y) div64_u64((X) + (Y - 1), Y)
/**
* sched_get_nr_running_avg
* @return: Average nr_running, iowait and nr_big_tasks value since last poll.
@@ -41,7 +43,8 @@
* Obtains the average nr_running value since the last poll.
* This function may not be called concurrently with itself
*/
-void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg)
+void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg,
+ unsigned int *max_nr, unsigned int *big_max_nr)
{
int cpu;
u64 curr_time = sched_clock();
@@ -51,6 +54,8 @@
*avg = 0;
*iowait_avg = 0;
*big_avg = 0;
+ *max_nr = 0;
+ *big_max_nr = 0;
if (!diff)
return;
@@ -79,17 +84,35 @@
per_cpu(nr_big_prod_sum, cpu) = 0;
per_cpu(iowait_prod_sum, cpu) = 0;
+ if (*max_nr < per_cpu(nr_max, cpu))
+ *max_nr = per_cpu(nr_max, cpu);
+
+ if (is_max_capacity_cpu(cpu)) {
+ if (*big_max_nr < per_cpu(nr_max, cpu))
+ *big_max_nr = per_cpu(nr_max, cpu);
+ }
+
+ per_cpu(nr_max, cpu) = per_cpu(nr, cpu);
spin_unlock_irqrestore(&per_cpu(nr_lock, cpu), flags);
}
diff = curr_time - last_get_time;
last_get_time = curr_time;
- *avg = (int)div64_u64(tmp_avg * 100, diff);
- *big_avg = (int)div64_u64(tmp_big_avg * 100, diff);
- *iowait_avg = (int)div64_u64(tmp_iowait * 100, diff);
+ /*
+ * Any task running on BIG cluster and BIG tasks running on little
+ * cluster contributes to big_avg. Small or medium tasks can also
+ * run on BIG cluster when co-location and scheduler boost features
+ * are activated. We don't want these tasks to downmigrate to little
+ * cluster when BIG CPUs are available but isolated. Round up the
+ * average values so that core_ctl aggressively unisolate BIG CPUs.
+ */
+ *avg = (int)DIV64_U64_ROUNDUP(tmp_avg, diff);
+ *big_avg = (int)DIV64_U64_ROUNDUP(tmp_big_avg, diff);
+ *iowait_avg = (int)DIV64_U64_ROUNDUP(tmp_iowait, diff);
- trace_sched_get_nr_running_avg(*avg, *big_avg, *iowait_avg);
+ trace_sched_get_nr_running_avg(*avg, *big_avg, *iowait_avg,
+ *max_nr, *big_max_nr);
BUG_ON(*avg < 0 || *big_avg < 0 || *iowait_avg < 0);
pr_debug("%s - avg:%d big_avg:%d iowait_avg:%d\n",
@@ -122,6 +145,9 @@
BUG_ON((s64)per_cpu(nr, cpu) < 0);
+ if (per_cpu(nr, cpu) > per_cpu(nr_max, cpu))
+ per_cpu(nr_max, cpu) = per_cpu(nr, cpu);
+
per_cpu(nr_prod_sum, cpu) += nr_running * diff;
per_cpu(nr_big_prod_sum, cpu) += nr_eligible_big_tasks(cpu) * diff;
per_cpu(iowait_prod_sum, cpu) += nr_iowait_cpu(cpu) * diff;
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index b89abbd..b38ec53 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -152,6 +152,8 @@
* IMPORTANT: Initialize both copies to same value!!
*/
+static __read_mostly bool sched_predl;
+
__read_mostly unsigned int sched_ravg_hist_size = 5;
__read_mostly unsigned int sysctl_sched_ravg_hist_size = 5;
@@ -231,6 +233,16 @@
early_param("sched_ravg_window", set_sched_ravg_window);
+static int __init set_sched_predl(char *str)
+{
+ unsigned int predl;
+
+ get_option(&str, &predl);
+ sched_predl = !!predl;
+ return 0;
+}
+early_param("sched_predl", set_sched_predl);
+
void inc_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra)
{
inc_nr_big_task(&rq->hmp_stats, p);
@@ -402,7 +414,7 @@
{
struct rq *rq = cpu_rq(cpu);
- if (cpu_max_possible_capacity(cpu) != max_possible_capacity)
+ if (!is_max_capacity_cpu(cpu))
return rq->hmp_stats.nr_big_tasks;
return rq->nr_running;
@@ -1096,6 +1108,9 @@
{
u32 new, old;
+ if (!sched_predl)
+ return;
+
if (is_idle_task(p) || exiting_task(p))
return;
@@ -1618,6 +1633,9 @@
int bidx;
u32 pred_demand;
+ if (!sched_predl)
+ return 0;
+
bidx = busy_to_bucket(runtime);
pred_demand = get_pred_busy(rq, p, bidx, runtime);
bucket_increase(p->ravg.busy_buckets, bidx);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index dedf4dc..eb556f8 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5902,8 +5902,6 @@
CF_MIN_3DB_150HZ << 5);
/* Enable TX PGA Mute */
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
- /* Enable APC */
- snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x08);
break;
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00);
@@ -5930,7 +5928,6 @@
hpf_cut_off_freq =
tasha->tx_hpf_work[decimator].hpf_cut_off_freq;
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
- snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x00);
if (cancel_delayed_work_sync(
&tasha->tx_hpf_work[decimator].dwork)) {
if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index d2ac038..083887b 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -13,7 +13,8 @@
pcm.o \
proc.o \
quirks.o \
- stream.o
+ stream.o \
+ badd.o
snd-usbmidi-lib-objs := midi.o
diff --git a/sound/usb/badd.c b/sound/usb/badd.c
new file mode 100644
index 0000000..cc6c26c
--- /dev/null
+++ b/sound/usb/badd.c
@@ -0,0 +1,137 @@
+/*
+ * 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/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
+
+struct uac3_input_terminal_descriptor badd_baif_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_input_terminal_descriptor badd_baof_in_term_desc = {
+ .bLength = UAC3_DT_INPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
+ .bTerminalID = BADD_IN_TERM_ID_BAOF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000,
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baif_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAIF,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
+ .bAssocTerminal = 0x00, /* No associated terminal */
+ .bSourceID = BADD_FU_ID_BAIF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000000, /* No controls */
+ .wExTerminalDescrID = 0x0000,
+ .wConnectorsDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+struct uac3_output_terminal_descriptor badd_baof_out_term_desc = {
+ .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
+ .bTerminalID = BADD_OUT_TERM_ID_BAOF,
+ .bSourceID = BADD_FU_ID_BAOF,
+ .bCSourceID = BADD_CLOCK_SOURCE,
+ .wExTerminalDescrID = 0x0000,
+ .wTerminalDescrStr = 0x0000
+};
+
+__u8 monoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00};
+
+__u8 stereoControls[] = {
+ 0x03, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00
+};
+
+__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF};
+
+struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = {
+ .bLength = UAC3_DT_MIXER_UNIT_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_MIXER_UNIT_V3,
+ .bUnitID = BADD_MU_ID_BAIOF,
+ .bNrInPins = 0x02,
+ .baSourceID = badd_mu_src_ids,
+ .bmMixerControls = 0x00,
+ .bmControls = 0x00000000,
+ .wMixerDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baif_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baof_fu_desc = {
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAOF,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_feature_unit_descriptor badd_baiof_fu_desc = {
+ .bLength = 0x0f,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3,
+ .bUnitID = BADD_FU_ID_BAIOF,
+ .bSourceID = BADD_IN_TERM_ID_BAIF,
+ .bmaControls = monoControls,
+ .wFeatureDescrStr = 0x0000
+};
+
+struct uac3_clock_source_descriptor badd_clock_desc = {
+ .bLength = UAC3_DT_CLOCK_SRC_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = UAC3_CLOCK_SOURCE,
+ .bClockID = BADD_CLOCK_SOURCE,
+ .bmControls = 0x00000001,
+ .bReferenceTerminal = 0x00,
+ .wClockSourceStr = 0x0000
+};
+
+void *badd_desc_list[] = {
+ &badd_baif_in_term_desc,
+ &badd_baof_in_term_desc,
+ &badd_baiof_mu_desc,
+ &badd_baif_fu_desc,
+ &badd_baof_fu_desc,
+ &badd_baiof_fu_desc,
+ &badd_clock_desc
+};
+
diff --git a/sound/usb/card.c b/sound/usb/card.c
index ccf06de..eaf18aa 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -45,6 +45,7 @@
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/module.h>
+#include <linux/usb/audio-v3.h>
#include <sound/control.h>
#include <sound/core.h>
@@ -285,7 +286,6 @@
struct usb_host_interface *host_iface;
struct usb_interface_descriptor *altsd;
struct usb_interface *usb_iface;
- void *control_header;
int i, protocol;
usb_iface = usb_ifnum_to_if(dev, ctrlif);
@@ -302,16 +302,13 @@
return -EINVAL;
}
- control_header = snd_usb_find_csint_desc(host_iface->extra,
- host_iface->extralen,
- NULL, UAC_HEADER);
altsd = get_iface_desc(host_iface);
protocol = altsd->bInterfaceProtocol;
- if (!control_header) {
- dev_err(&dev->dev, "cannot find UAC_HEADER\n");
- return -EINVAL;
- }
+ /*
+ * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces;
+ * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces
+ */
switch (protocol) {
default:
@@ -321,8 +318,17 @@
/* fall through */
case UAC_VERSION_1: {
- struct uac1_ac_header_descriptor *h1 = control_header;
+ void *control_header;
+ struct uac1_ac_header_descriptor *h1;
+ control_header = snd_usb_find_csint_desc(host_iface->extra,
+ host_iface->extralen, NULL, UAC_HEADER);
+ if (!control_header) {
+ dev_err(&dev->dev, "cannot find UAC_HEADER\n");
+ return -EINVAL;
+ }
+
+ h1 = control_header;
if (!h1->bInCollection) {
dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
return -EINVAL;
@@ -339,7 +345,8 @@
break;
}
- case UAC_VERSION_2: {
+ case UAC_VERSION_2:
+ case UAC_VERSION_3: {
struct usb_interface_assoc_descriptor *assoc =
usb_iface->intf_assoc;
if (!assoc) {
@@ -358,7 +365,8 @@
}
if (!assoc) {
- dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
+ dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n",
+ protocol);
return -EINVAL;
}
@@ -606,6 +614,15 @@
struct usb_host_interface *alts;
int ifnum;
u32 id;
+ struct usb_interface_assoc_descriptor *assoc;
+
+ assoc = intf->intf_assoc;
+ if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO &&
+ assoc->bFunctionProtocol == UAC_VERSION_3 &&
+ assoc->bFunctionSubClass == FULL_ADC_PROFILE) {
+ dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n");
+ return -EINVAL;
+ }
alts = &intf->altsetting[0];
ifnum = get_iface_desc(alts)->bInterfaceNumber;
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 26dd5f2..8238180 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -428,6 +428,10 @@
case UAC_VERSION_2:
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+
+ /* Clock rate is fixed at 48 kHz for BADD devices */
+ case UAC_VERSION_3:
+ return 0;
}
}
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 2c44386..eaf2615 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -69,6 +70,34 @@
format <<= 1;
break;
}
+
+ case UAC_VERSION_3: {
+ switch (fp->maxpacksize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ sample_width = BIT_RES_16_BIT;
+ sample_bytes = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ sample_width = BIT_RES_24_BIT;
+ sample_bytes = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+ default:
+ usb_audio_err(chip, "%u:%d : Invalid wMaxPacketSize\n",
+ fp->iface, fp->altsetting);
+ return pcm_formats;
+ }
+ format = 1 << format;
+ break;
+ }
}
if ((pcm_formats == 0) &&
@@ -364,17 +393,34 @@
return ret;
}
+static int badd_set_audio_rate_v3(struct snd_usb_audio *chip,
+ struct audioformat *fp)
+{
+ unsigned int rate;
+
+ fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
+ if (fp->rate_table == NULL)
+ return -ENOMEM;
+
+ fp->nr_rates = 1;
+ rate = BADD_SAMPLING_RATE;
+ fp->rate_min = fp->rate_max = fp->rate_table[0] = rate;
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+ return 0;
+}
+
/*
* parse the format type I and III descriptors
*/
static int parse_audio_format_i(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format,
+ u8 format_type,
struct uac_format_type_i_continuous_descriptor *fmt)
{
snd_pcm_format_t pcm_format;
int ret;
- if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
+ if (format_type == UAC_FORMAT_TYPE_III) {
/* FIXME: the format type is really IECxxx
* but we give normal PCM format to get the existing
* apps working...
@@ -413,6 +459,9 @@
/* fp->channels is already set in this case */
ret = parse_audio_format_rates_v2(chip, fp);
break;
+ case UAC_VERSION_3:
+ ret = badd_set_audio_rate_v3(chip, fp);
+ break;
}
if (fp->channels < 1) {
@@ -484,11 +533,18 @@
int stream)
{
int err;
+ int format_type = -EINVAL;
- switch (fmt->bFormatType) {
+ if ((fp->protocol == UAC_VERSION_1) ||
+ (fp->protocol == UAC_VERSION_2))
+ format_type = fmt->bFormatType;
+ else
+ format_type = UAC_FORMAT_TYPE_I; /* only BADD is supported */
+
+ switch (format_type) {
case UAC_FORMAT_TYPE_I:
case UAC_FORMAT_TYPE_III:
- err = parse_audio_format_i(chip, fp, format, fmt);
+ err = parse_audio_format_i(chip, fp, format, format_type, fmt);
break;
case UAC_FORMAT_TYPE_II:
err = parse_audio_format_ii(chip, fp, format, fmt);
@@ -497,10 +553,10 @@
usb_audio_info(chip,
"%u:%d : format type %d is not supported yet\n",
fp->iface, fp->altsetting,
- fmt->bFormatType);
+ format_type);
return -ENOTSUPP;
}
- fp->fmt_type = fmt->bFormatType;
+ fp->fmt_type = format_type;
if (err < 0)
return err;
#if 1
@@ -511,7 +567,7 @@
if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
chip->usb_id == USB_ID(0x041e, 0x3020) ||
chip->usb_id == USB_ID(0x041e, 0x3061)) {
- if (fmt->bFormatType == UAC_FORMAT_TYPE_I &&
+ if (format_type == UAC_FORMAT_TYPE_I &&
fp->rates != SNDRV_PCM_RATE_48000 &&
fp->rates != SNDRV_PCM_RATE_96000)
return -ENOTSUPP;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 932ce3e..c3bf5ff 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -51,6 +51,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -185,6 +186,17 @@
/* we just parse the header */
struct uac_feature_unit_descriptor *hdr = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ int i;
+
+ for (i = 0; i < NUM_BADD_DESCS; i++) {
+ hdr = (void *)badd_desc_list[i];
+ if (hdr->bUnitID == unit)
+ return hdr;
+ }
+
+ return NULL;
+ }
while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
@@ -718,7 +730,7 @@
term->channels = d->bNrChannels;
term->chconfig = le16_to_cpu(d->wChannelConfig);
term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
@@ -735,6 +747,24 @@
term->channels = d->bNrChannels;
term->chconfig = le32_to_cpu(d->bmChannelConfig);
term->name = d->iTerminal;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_input_terminal_descriptor *d = p1;
+
+ err = check_input_term(state,
+ d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ term->id = id;
+ term->type = d->wTerminalType;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wTerminalDescrStr;
}
return 0;
case UAC_FEATURE_UNIT: {
@@ -752,41 +782,81 @@
return 0;
}
case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
+ /* UAC3_MIXER_UNIT_V3 */
+ case UAC2_CLOCK_SELECTOR:
+ /* UAC3_CLOCK_SOURCE */ {
+ if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_CLOCK_SOURCE) {
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = d->wClockSourceStr;
+ } else if (state->mixer->protocol == UAC_VERSION_3
+ && hdr[2] == UAC3_MIXER_UNIT_V3) {
+ struct uac3_mixer_unit_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16;
+ if (d->wClusterDescrID == CLUSTER_ID_MONO) {
+ term->channels = NUM_CHANNELS_MONO;
+ term->chconfig = BADD_CH_CONFIG_MONO;
+ } else {
+ term->channels = NUM_CHANNELS_STEREO;
+ term->chconfig = BADD_CH_CONFIG_STEREO;
+ }
+ term->name = d->wMixerDescrStr;
+ } else {
+ struct uac_selector_unit_descriptor *d = p1;
+ /* call recursively to retrieve channel info */
+ err = check_input_term(state,
+ d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->id = id;
+ term->name = uac_selector_unit_iSelector(d);
+ }
return 0;
}
case UAC1_PROCESSING_UNIT:
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 */
/* UAC2_EFFECT_UNIT */
+ /* UAC3_FEATURE_UNIT_V3 */
case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ struct uac_feature_unit_descriptor *d = p1;
- if (state->mixer->protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
+ id = d->bSourceID;
+ } else {
+ struct uac_processing_unit_descriptor *d = p1;
+
+ if (state->mixer->protocol == UAC_VERSION_2 &&
+ hdr[2] == UAC2_EFFECT_UNIT) {
+ /* UAC2/UAC1 unit IDs overlap here in an
+ * uncompatible way. Ignore this unit
+ * for now.
+ */
+ return 0;
+ }
+
+ if (d->bNrInPins) {
+ id = d->baSourceID[0];
+ break; /* continue to parse */
+ }
+ /* virtual type */
+ term->type = d->bDescriptorSubtype << 16;
+ term->channels =
+ uac_processing_unit_bNrChannels(d);
+ term->chconfig =
+ uac_processing_unit_wChannelConfig(
+ d, state->mixer->protocol);
+ term->name = uac_processing_unit_iProcessing(
+ d, state->mixer->protocol);
return 0;
}
-
- if (d->bNrInPins) {
- id = d->baSourceID[0];
- break; /* continue to parse */
- }
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
- return 0;
+ break;
}
case UAC2_CLOCK_SOURCE: {
struct uac_clock_source_descriptor *d = p1;
@@ -1233,12 +1303,18 @@
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
int mapped_name = 0;
- int nameid = uac_feature_unit_iFeature(desc);
+ int nameid;
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
unsigned int range;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ nameid = ((struct uac3_feature_unit_descriptor *)
+ raw_desc)->wFeatureDescrStr;
+ else
+ nameid = uac_feature_unit_iFeature(desc);
+
control++; /* change from zero-based to 1-based value */
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
@@ -1259,7 +1335,7 @@
ctl_info = &audio_feature_info[control-1];
if (state->mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
- else /* UAC_VERSION_2 */
+ else /* UAC_VERSION_2 or UAC_VERSION_3*/
cval->val_type = ctl_info->type_uac2 >= 0 ?
ctl_info->type_uac2 : ctl_info->type;
@@ -1447,6 +1523,62 @@
return snd_usb_mixer_add_control(&cval->head, kctl);
}
+static int find_num_channels(struct mixer_build *state, int dir)
+{
+ int num_ch = -EINVAL, num, i, j, wMaxPacketSize;
+ int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber;
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, ctrlif);
+ struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc;
+ struct usb_host_interface *alts;
+
+ for (i = 0; i < assoc->bInterfaceCount; i++) {
+ int intf = assoc->bFirstInterface + i;
+
+ if (intf != ctrlif) {
+ struct usb_interface *iface =
+ usb_ifnum_to_if(state->mixer->chip->dev, intf);
+
+ alts = &iface->altsetting[1];
+ if (dir == USB_DIR_OUT &&
+ get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN)
+ continue;
+ if (dir == USB_DIR_IN &&
+ !(get_endpoint(alts, 0)->bEndpointAddress &
+ USB_DIR_IN))
+ continue;
+ num = iface->num_altsetting;
+ for (j = 1; j < num; j++) {
+ num_ch = NUM_CHANNELS_MONO;
+ alts = &iface->altsetting[j];
+ wMaxPacketSize = le16_to_cpu(
+ get_endpoint(alts, 0)->
+ wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ break;
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24:
+ num_ch = NUM_CHANNELS_STEREO;
+ break;
+ }
+ if (num_ch == NUM_CHANNELS_MONO)
+ continue;
+ else
+ break;
+ }
+ }
+ }
+
+ return num_ch;
+}
+
/*
* parse a feature unit
*
@@ -1478,7 +1610,7 @@
unitid);
return -EINVAL;
}
- } else {
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
@@ -1489,11 +1621,118 @@
unitid);
return -EINVAL;
}
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ csize = 4;
+ switch (unitid) {
+ case BADD_FU_ID_BAIOF:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADPHONE:
+ case PROF_HEADSET_ADAPTER:
+ channels = NUM_CHANNELS_STEREO;
+ bmaControls = stereoControls;
+ badd_baiof_mu_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state,
+ USB_DIR_OUT);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baof_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+
+ case BADD_FU_ID_BAIF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ case PROF_SPEAKERPHONE:
+ channels = NUM_CHANNELS_MONO;
+ bmaControls = monoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ CLUSTER_ID_MONO;
+ break;
+ default:
+ channels = find_num_channels(state, USB_DIR_IN);
+ if (channels < 0) {
+ usb_audio_err(state->chip,
+ "unit %u: Cant find num of channels\n",
+ unitid);
+ return channels;
+ }
+
+ bmaControls = (channels == NUM_CHANNELS_MONO) ?
+ monoControls : stereoControls;
+ badd_baif_in_term_desc.wClusterDescrID =
+ (channels == NUM_CHANNELS_MONO) ?
+ CLUSTER_ID_MONO : CLUSTER_ID_STEREO;
+ break;
+ }
+ break;
+
+ default:
+ usb_audio_err(state->chip, "Invalid unit %u\n", unitid);
+ return -EINVAL;
+ }
}
/* parse the source unit */
- if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
- return err;
+ if (state->mixer->protocol != UAC_VERSION_3) {
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ } else {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(state->mixer->chip->dev,
+ get_iface_desc(state->mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
+
+ switch (unitid) {
+ case BADD_FU_ID_BAOF:
+ switch (assoc->bFunctionSubClass) {
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ hdr->bSourceID = BADD_MU_ID_BAIOF;
+ break;
+ default:
+ hdr->bSourceID = BADD_IN_TERM_ID_BAOF;
+ break;
+ }
+ }
+ err = parse_audio_unit(state, hdr->bSourceID);
+ if (err < 0)
+ return err;
+ }
/* determine the input source type and name */
err = check_input_term(state, hdr->bSourceID, &iterm);
@@ -1547,7 +1786,7 @@
build_feature_ctl(state, _ftr, 0, i, &iterm,
unitid, 0);
}
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2 or UAC_VERSION_3*/
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
@@ -1665,12 +1904,20 @@
int input_pins, num_ins, num_outs;
int pin, ich, err;
- if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
- !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
- usb_audio_err(state->chip,
- "invalid MIXER UNIT descriptor %d\n",
- unitid);
- return -EINVAL;
+ if (state->mixer->protocol == UAC_VERSION_3) {
+ input_pins = badd_baiof_mu_desc.bNrInPins;
+ num_outs =
+ (badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ?
+ NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO;
+ } else {
+ input_pins = desc->bNrInPins;
+ num_outs = uac_mixer_unit_bNrChannels(desc);
+ if (desc->bLength < 11 || !input_pins || !num_outs) {
+ usb_audio_err(state->chip,
+ "invalid MIXER UNIT descriptor %d\n",
+ unitid);
+ return -EINVAL;
+ }
}
num_ins = 0;
@@ -1690,9 +1937,14 @@
int och, ich_has_controls = 0;
for (och = 0; och < num_outs; och++) {
- __u8 *c = uac_mixer_unit_bmControls(desc,
- state->mixer->protocol);
+ __u8 *c = NULL;
+ if (state->mixer->protocol == UAC_VERSION_3)
+ c =
+ &(badd_baiof_mu_desc.bmMixerControls);
+ else
+ c = uac_mixer_unit_bmControls(desc,
+ state->mixer->protocol);
if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
break;
@@ -2201,16 +2453,28 @@
case UAC2_CLOCK_SOURCE:
return parse_clock_source_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT:
+ /* UAC3_MIXER_UNIT_V3 has the same value */
case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
+ /* UAC3_CLOCK_SOURCE has the same value */
+ if (state->mixer->protocol == UAC_VERSION_3 &&
+ p1[2] == UAC3_CLOCK_SOURCE)
+ return 0; /* NOP */
+ else if (state->mixer->protocol == UAC_VERSION_3
+ && p1[2] == UAC3_MIXER_UNIT_V3)
+ return parse_audio_mixer_unit(state, unitid, p1);
+ else
+ return parse_audio_selector_unit(state, unitid, p1);
case UAC_FEATURE_UNIT:
return parse_audio_feature_unit(state, unitid, p1);
case UAC1_PROCESSING_UNIT:
/* UAC2_EFFECT_UNIT has the same value */
+ /* UAC3_FEATURE_UNIT_V3 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
- else
+ else if (state->mixer->protocol == UAC_VERSION_2)
return 0; /* FIXME - effect units not implemented yet */
+ else
+ return parse_audio_feature_unit(state, unitid, p1);
case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
@@ -2245,6 +2509,23 @@
return 0;
}
+static int make_out_term(struct mixer_build state, int wTerminalType)
+{
+ struct uac3_output_terminal_descriptor *desc = NULL;
+
+ if (wTerminalType == UAC_TERMINAL_STREAMING)
+ desc = &badd_baif_out_term_desc;
+ else {
+ desc = &badd_baof_out_term_desc;
+ desc->wTerminalType = wTerminalType;
+ }
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type = desc->wTerminalType;
+ state.oterm.name = desc->wTerminalDescrStr;
+ return parse_audio_unit(&state, desc->bSourceID);
+}
+
/*
* create mixer controls
*
@@ -2253,9 +2534,8 @@
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{
struct mixer_build state;
- int err;
+ int err = -EINVAL;
const struct usbmix_ctl_map *map;
- void *p;
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
@@ -2273,44 +2553,108 @@
}
}
- p = NULL;
- while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
- mixer->hostif->extralen,
- p, UAC_OUTPUT_TERMINAL)) != NULL) {
- if (mixer->protocol == UAC_VERSION_1) {
- struct uac1_output_terminal_descriptor *desc = p;
+ if (mixer->protocol == UAC_VERSION_3) {
+ struct usb_interface *usb_iface =
+ usb_ifnum_to_if(mixer->chip->dev,
+ get_iface_desc(mixer->hostif)->bInterfaceNumber);
+ struct usb_interface_assoc_descriptor *assoc =
+ usb_iface->intf_assoc;
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ switch (assoc->bFunctionSubClass) {
+ case PROF_GENERIC_IO: {
+ if (assoc->bInterfaceCount == 0x02) {
+ if (get_endpoint(mixer->hostif,
+ 0)->bEndpointAddress | USB_DIR_IN)
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ else
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ } else {
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_UNDEFINED);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ err = make_out_term(state,
+ UAC_TERMINAL_STREAMING);
+ }
+ break;
+ }
+
+ case PROF_HEADPHONE:
+ err = make_out_term(state,
+ UAC_OUTPUT_TERMINAL_HEADPHONES);
+ break;
+ case PROF_SPEAKER:
+ err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER);
+ break;
+ case PROF_MICROPHONE:
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_HEADSET:
+ case PROF_HEADSET_ADAPTER:
+ err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET);
if (err < 0 && err != -EINVAL)
return err;
- } else { /* UAC_VERSION_2 */
- struct uac2_output_terminal_descriptor *desc = p;
-
- if (desc->bLength < sizeof(*desc))
- continue; /* invalid descriptor? */
- /* mark terminal ID as visited */
- set_bit(desc->bTerminalID, state.unitbitmap);
- state.oterm.id = desc->bTerminalID;
- state.oterm.type = le16_to_cpu(desc->wTerminalType);
- state.oterm.name = desc->iTerminal;
- err = parse_audio_unit(&state, desc->bSourceID);
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ case PROF_SPEAKERPHONE:
+ err = make_out_term(state,
+ UAC_BIDIR_TERMINAL_SPEAKERPHONE);
if (err < 0 && err != -EINVAL)
return err;
+ err = make_out_term(state, UAC_TERMINAL_STREAMING);
+ break;
+ }
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else {
+ void *p;
- /*
- * For UAC2, use the same approach to also add the
- * clock selectors
- */
- err = parse_audio_unit(&state, desc->bCSourceID);
- if (err < 0 && err != -EINVAL)
- return err;
+ p = NULL;
+ while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
+ mixer->hostif->extralen, p,
+ UAC_OUTPUT_TERMINAL)) != NULL) {
+ if (mixer->protocol == UAC_VERSION_1) {
+ struct uac1_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ } else { /* UAC_VERSION_2 */
+ struct uac2_output_terminal_descriptor *desc =
+ p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type =
+ le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = desc->iTerminal;
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+
+ /*
+ * For UAC2, use the same approach to also add
+ * the clock selectors
+ */
+ err = parse_audio_unit(&state,
+ desc->bCSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+ }
}
}
@@ -2552,6 +2896,9 @@
case UAC_VERSION_2:
mixer->protocol = UAC_VERSION_2;
break;
+ case UAC_VERSION_3:
+ mixer->protocol = UAC_VERSION_3;
+ break;
}
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 7437cd5..5bc84b4 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -20,6 +20,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -282,8 +283,6 @@
0 /* terminator */
};
struct snd_pcm_chmap_elem *chmap;
- const unsigned int *maps;
- int c;
if (channels > ARRAY_SIZE(chmap->map))
return NULL;
@@ -292,26 +291,41 @@
if (!chmap)
return NULL;
- maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
chmap->channels = channels;
- c = 0;
- if (bits) {
- for (; bits && *maps; maps++, bits >>= 1)
- if (bits & 1)
- chmap->map[c++] = *maps;
+ if (protocol == UAC_VERSION_3) {
+ switch (channels) {
+ case 1:
+ chmap->map[0] = SNDRV_CHMAP_MONO;
+ break;
+ case 2:
+ chmap->map[0] = SNDRV_CHMAP_FL;
+ chmap->map[1] = SNDRV_CHMAP_FR;
+ break;
+ }
} else {
- /* If we're missing wChannelConfig, then guess something
- to make sure the channel map is not skipped entirely */
- if (channels == 1)
- chmap->map[c++] = SNDRV_CHMAP_MONO;
- else
- for (; c < channels && *maps; maps++)
- chmap->map[c++] = *maps;
- }
+ int c = 0;
+ const unsigned int *maps =
+ protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
- for (; c < channels; c++)
- chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
+ if (bits) {
+ for (; bits && *maps; maps++, bits >>= 1)
+ if (bits & 1)
+ chmap->map[c++] = *maps;
+ } else {
+ /*
+ * If we're missing wChannelConfig, then guess something
+ * to make sure the channel map is not skipped entirely
+ */
+ if (channels == 1)
+ chmap->map[c++] = SNDRV_CHMAP_MONO;
+ else
+ for (; c < channels && *maps; maps++)
+ chmap->map[c++] = *maps;
+ }
+ for (; c < channels; c++)
+ chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
+ }
return chmap;
}
@@ -409,6 +423,9 @@
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
int attributes = 0;
+ if (protocol == UAC_VERSION_3)
+ return 0;
+
csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
/* Creamware Noah has this descriptor after the 2nd endpoint */
@@ -492,7 +509,7 @@
unsigned int format = 0, num_channels = 0;
struct audioformat *fp = NULL;
int num, protocol, clock = 0;
- struct uac_format_type_i_continuous_descriptor *fmt;
+ struct uac_format_type_i_continuous_descriptor *fmt = NULL;
unsigned int chconfig;
dev = chip->dev;
@@ -629,38 +646,78 @@
iface_no, altno, as->bTerminalLink);
continue;
}
+
+ case UAC_VERSION_3: {
+ int wMaxPacketSize;
+
+ format = UAC_FORMAT_TYPE_I_PCM;
+ clock = BADD_CLOCK_SOURCE;
+ wMaxPacketSize = le16_to_cpu(get_endpoint(alts, 0)
+ ->wMaxPacketSize);
+ switch (wMaxPacketSize) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_24: {
+ num_channels = NUM_CHANNELS_MONO;
+ chconfig = BADD_CH_CONFIG_MONO;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ num_channels = NUM_CHANNELS_STEREO;
+ chconfig = BADD_CH_CONFIG_STEREO;
+ break;
+ }
+ default:
+ dev_err(&dev->dev,
+ "%u:%d: invalid wMaxPacketSize\n",
+ iface_no, altno);
+ continue;
+ }
+ }
}
- /* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
- if (!fmt) {
- dev_err(&dev->dev,
- "%u:%d : no UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
- continue;
- }
- if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
- ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
- dev_err(&dev->dev,
- "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
- iface_no, altno);
- continue;
- }
+ if ((protocol == UAC_VERSION_1) ||
+ (protocol == UAC_VERSION_2)) {
+ /* get format type */
+ fmt = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen, NULL, UAC_FORMAT_TYPE);
+ if (!fmt) {
+ dev_err(&dev->dev,
+ "%u:%d : no UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
+ if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8))
+ || ((protocol == UAC_VERSION_2) &&
+ (fmt->bLength < 6))) {
+ dev_err(&dev->dev,
+ "%u:%d :invalid UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
- /*
- * Blue Microphones workaround: The last altsetting is identical
- * with the previous one, except for a larger packet size, but
- * is actually a mislabeled two-channel setting; ignore it.
- */
- if (fmt->bNrChannels == 1 &&
- fmt->bSubframeSize == 2 &&
- altno == 2 && num == 3 &&
- fp && fp->altsetting == 1 && fp->channels == 1 &&
- fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
- protocol == UAC_VERSION_1 &&
- le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+ /*
+ * Blue Microphones workaround: The last altsetting is
+ * identical with the previous one, except for a larger
+ * packet size, but is actually a mislabeled two-channel
+ * setting; ignore it.
+ */
+ if (fmt->bNrChannels == 1 &&
+ fmt->bSubframeSize == 2 &&
+ altno == 2 && num == 3 &&
+ fp && fp->altsetting == 1 && fp->channels == 1 &&
+ fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+ protocol == UAC_VERSION_1 &&
+ le16_to_cpu(
+ get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
- continue;
+ continue;
+ }
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 5a1974e..801508c 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -27,6 +27,7 @@
#include <soc/qcom/msm_qmi_interface.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
+#include <linux/usb/audio-v3.h>
#include "usbaudio.h"
#include "card.h"
@@ -427,12 +428,14 @@
protocol = altsd->bInterfaceProtocol;
/* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
- UAC_FORMAT_TYPE);
- if (!fmt) {
- pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__,
- subs->interface, subs->altset_idx);
- goto err;
+ if (protocol != UAC_VERSION_3) {
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_FORMAT_TYPE);
+ if (!fmt) {
+ pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n",
+ __func__, subs->interface, subs->altset_idx);
+ goto err;
+ }
}
if (!uadev[card_num].ctrl_intf) {
@@ -440,12 +443,15 @@
goto err;
}
- hdr_ptr = snd_usb_find_csint_desc(uadev[card_num].ctrl_intf->extra,
- uadev[card_num].ctrl_intf->extralen,
- NULL, UAC_HEADER);
- if (!hdr_ptr) {
- pr_err("%s: no UAC_HEADER desc\n", __func__);
- goto err;
+ if (protocol != UAC_VERSION_3) {
+ hdr_ptr = snd_usb_find_csint_desc(
+ uadev[card_num].ctrl_intf->extra,
+ uadev[card_num].ctrl_intf->extralen,
+ NULL, UAC_HEADER);
+ if (!hdr_ptr) {
+ pr_err("%s: no UAC_HEADER desc\n", __func__);
+ goto err;
+ }
}
if (protocol == UAC_VERSION_1) {
@@ -473,6 +479,31 @@
resp->usb_audio_spec_revision =
((struct uac2_ac_header_descriptor *)hdr_ptr)->bcdADC;
resp->usb_audio_spec_revision_valid = 1;
+ } else if (protocol == UAC_VERSION_3) {
+ switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
+ case BADD_MAXPSIZE_SYNC_MONO_16:
+ case BADD_MAXPSIZE_SYNC_STEREO_16:
+ case BADD_MAXPSIZE_ASYNC_MONO_16:
+ case BADD_MAXPSIZE_ASYNC_STEREO_16: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_16_BIT;
+ break;
+ }
+
+ case BADD_MAXPSIZE_SYNC_MONO_24:
+ case BADD_MAXPSIZE_SYNC_STEREO_24:
+ case BADD_MAXPSIZE_ASYNC_MONO_24:
+ case BADD_MAXPSIZE_ASYNC_STEREO_24: {
+ resp->usb_audio_subslot_size = SUBSLOTSIZE_24_BIT;
+ break;
+ }
+
+ default:
+ pr_err("%d: %u: Invalid wMaxPacketSize\n",
+ subs->interface, subs->altset_idx);
+ ret = -EINVAL;
+ goto err;
+ }
+ resp->usb_audio_subslot_size_valid = 1;
} else {
pr_err("%s: unknown protocol version %x\n", __func__, protocol);
goto err;