Merge "clk: qcom: gcc-sdxpoorwills: add GCC_SYS_NOC_USB3_CLK clock" into msm-4.9
diff --git a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
index 122299c..f27b9da 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-cdp.dts
@@ -22,7 +22,7 @@
qcom,board-id = <1 1>;
};
-&dsi_dual_nt35597_truly_video_display {
+&dsi_dual_nt35597_truly_cmd_display {
/delete-property/ qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
index 55e615c..4627e60 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-mtp.dts
@@ -22,7 +22,7 @@
qcom,board-id = <8 1>;
};
-&dsi_dual_nt35597_truly_video_display {
+&dsi_dual_nt35597_truly_cmd_display {
/delete-property/ qcom,dsi-display-active;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 4c642e3..51e1ccf 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -464,7 +464,7 @@
cell-index = <0>;
label = "cam-cdm-intf";
num-hw-cdm = <1>;
- cdm-client-names = "ife",
+ cdm-client-names = "vfe",
"jpeg-dma",
"jpeg",
"fd";
@@ -493,7 +493,7 @@
<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
clock-rates = <0 0 0 0 0>;
- cdm-client-names = "vfe";
+ cdm-client-names = "ife";
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 1b3f2a6..faf43fca 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -237,7 +237,7 @@
label = "kgsl-gmu";
compatible = "qcom,gpu-gmu";
- reg = <0x506a000 0x26000>, <0xb200000 0x300000>;
+ reg = <0x506a000 0x30000>, <0xb200000 0x300000>;
reg-names = "kgsl_gmu_reg", "kgsl_gmu_pdc_reg";
interrupts = <0 304 0>, <0 305 0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index da5d6fa..c7a4d7d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -39,44 +39,44 @@
36 37>;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0xffffffff>;
- interrupt-map = <0 0 0 0 &intc 0 141 0
- 0 0 0 1 &intc 0 149 0
- 0 0 0 2 &intc 0 150 0
- 0 0 0 3 &intc 0 151 0
- 0 0 0 4 &intc 0 152 0
- 0 0 0 5 &intc 0 140 0
- 0 0 0 6 &intc 0 672 0
- 0 0 0 7 &intc 0 673 0
- 0 0 0 8 &intc 0 674 0
- 0 0 0 9 &intc 0 675 0
- 0 0 0 10 &intc 0 676 0
- 0 0 0 11 &intc 0 677 0
- 0 0 0 12 &intc 0 678 0
- 0 0 0 13 &intc 0 679 0
- 0 0 0 14 &intc 0 680 0
- 0 0 0 15 &intc 0 681 0
- 0 0 0 16 &intc 0 682 0
- 0 0 0 17 &intc 0 683 0
- 0 0 0 18 &intc 0 684 0
- 0 0 0 19 &intc 0 685 0
- 0 0 0 20 &intc 0 686 0
- 0 0 0 21 &intc 0 687 0
- 0 0 0 22 &intc 0 688 0
- 0 0 0 23 &intc 0 689 0
- 0 0 0 24 &intc 0 690 0
- 0 0 0 25 &intc 0 691 0
- 0 0 0 26 &intc 0 692 0
- 0 0 0 27 &intc 0 693 0
- 0 0 0 28 &intc 0 694 0
- 0 0 0 29 &intc 0 695 0
- 0 0 0 30 &intc 0 696 0
- 0 0 0 31 &intc 0 697 0
- 0 0 0 32 &intc 0 698 0
- 0 0 0 33 &intc 0 699 0
- 0 0 0 34 &intc 0 700 0
- 0 0 0 35 &intc 0 701 0
- 0 0 0 36 &intc 0 702 0
- 0 0 0 37 &intc 0 703 0>;
+ interrupt-map = <0 0 0 0 &pdc 0 141 0
+ 0 0 0 1 &pdc 0 149 0
+ 0 0 0 2 &pdc 0 150 0
+ 0 0 0 3 &pdc 0 151 0
+ 0 0 0 4 &pdc 0 152 0
+ 0 0 0 5 &pdc 0 140 0
+ 0 0 0 6 &pdc 0 672 0
+ 0 0 0 7 &pdc 0 673 0
+ 0 0 0 8 &pdc 0 674 0
+ 0 0 0 9 &pdc 0 675 0
+ 0 0 0 10 &pdc 0 676 0
+ 0 0 0 11 &pdc 0 677 0
+ 0 0 0 12 &pdc 0 678 0
+ 0 0 0 13 &pdc 0 679 0
+ 0 0 0 14 &pdc 0 680 0
+ 0 0 0 15 &pdc 0 681 0
+ 0 0 0 16 &pdc 0 682 0
+ 0 0 0 17 &pdc 0 683 0
+ 0 0 0 18 &pdc 0 684 0
+ 0 0 0 19 &pdc 0 685 0
+ 0 0 0 20 &pdc 0 686 0
+ 0 0 0 21 &pdc 0 687 0
+ 0 0 0 22 &pdc 0 688 0
+ 0 0 0 23 &pdc 0 689 0
+ 0 0 0 24 &pdc 0 690 0
+ 0 0 0 25 &pdc 0 691 0
+ 0 0 0 26 &pdc 0 692 0
+ 0 0 0 27 &pdc 0 693 0
+ 0 0 0 28 &pdc 0 694 0
+ 0 0 0 29 &pdc 0 695 0
+ 0 0 0 30 &pdc 0 696 0
+ 0 0 0 31 &pdc 0 697 0
+ 0 0 0 32 &pdc 0 698 0
+ 0 0 0 33 &pdc 0 699 0
+ 0 0 0 34 &pdc 0 700 0
+ 0 0 0 35 &pdc 0 701 0
+ 0 0 0 36 &pdc 0 702 0
+ 0 0 0 37 &pdc 0 703 0>;
interrupt-names = "int_msi", "int_a", "int_b", "int_c",
"int_d", "int_global_int",
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 1744574..cfbf3e5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -19,6 +19,7 @@
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
+ interrupt-parent = <&pdc>;
ufs_dev_reset_assert: ufs_dev_reset_assert {
config {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index 70e749b..6806145 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -145,4 +145,12 @@
reg = <0xC300000 0x1000>, <0xC3F0004 0x4>;
reg-names = "phys_addr_base", "offset_addr";
};
+
+ pdc: interrupt-controller@0xb220000{
+ compatible = "qcom,pdc-sdm845";
+ reg = <0xb220000 0x400>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
index 0fb455f..1fa6e26 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
@@ -42,7 +42,7 @@
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se6_4uart_active>;
pinctrl-1 = <&qupv3_se6_4uart_sleep>;
- interrupts-extended = <&intc GIC_SPI 607 0>,
+ interrupts-extended = <&pdc GIC_SPI 607 0>,
<&tlmm 48 0>;
status = "disabled";
qcom,wakeup-byte = <0xFD>;
@@ -60,7 +60,7 @@
pinctrl-names = "default", "sleep";
pinctrl-0 = <&qupv3_se7_4uart_active>;
pinctrl-1 = <&qupv3_se7_4uart_sleep>;
- interrupts-extended = <&intc GIC_SPI 608 0>,
+ interrupts-extended = <&pdc GIC_SPI 608 0>,
<&tlmm 96 0>;
status = "disabled";
qcom,wakeup-byte = <0xFD>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index e92bfd9..21819a9 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -35,7 +35,7 @@
sde-vdd-supply = <&mdss_core_gdsc>;
/* interrupt config */
- interrupt-parent = <&intc>;
+ interrupt-parent = <&pdc>;
interrupts = <0 83 0>;
interrupt-controller;
#interrupt-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 6fb6fb8..3870d8f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -67,7 +67,6 @@
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a600000 0xcd00>;
- interrupt-parent = <&intc>;
interrupts = <0 133 0>;
usb-phy = <&qusb_phy0>, <&usb_qmp_dp_phy>;
tx-fifo-resize;
@@ -80,7 +79,6 @@
qcom,usbbam@a704000 {
compatible = "qcom,usb-bam-msm";
reg = <0xa704000 0x17000>;
- interrupt-parent = <&intc>;
interrupts = <0 132 0>;
qcom,bam-type = <0>;
@@ -361,7 +359,6 @@
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a800000 0xcd00>;
- interrupt-parent = <&intc>;
interrupts = <0 138 0>;
usb-phy = <&qusb_phy1>, <&usb_qmp_phy>;
tx-fifo-resize;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index c806627..88586c0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -30,7 +30,7 @@
model = "Qualcomm Technologies, Inc. SDM845";
compatible = "qcom,sdm845";
qcom,msm-id = <321 0x0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&pdc>;
aliases {
ufshc1 = &ufshc_mem; /* Embedded UFS slot */
@@ -606,6 +606,7 @@
reg = <0x17a00000 0x10000>, /* GICD */
<0x17a60000 0x100000>; /* GICR * 8 */
interrupts = <1 9 4>;
+ interrupt-parent = <&intc>;
};
timer {
@@ -807,7 +808,8 @@
< 1881600 >,
< 1958400 >,
< 2035200 >,
- < 2092800 >;
+ < 2092800 >,
+ < 2208000 >;
};
cpubw: qcom,cpubw {
@@ -961,7 +963,9 @@
< 883200 >,
< 960000 >,
< 1036800 >,
- < 1094400 >;
+ < 1094400 >,
+ < 1209600 >,
+ < 1305600 >;
};
l3_cpu4: qcom,l3-cpu4 {
@@ -981,7 +985,9 @@
< 883200 >,
< 960000 >,
< 1036800 >,
- < 1094400 >;
+ < 1094400 >,
+ < 1209600 >,
+ < 1305600 >;
};
devfreq_l3lat_0: qcom,cpu0-l3lat-mon {
@@ -1041,8 +1047,8 @@
cpu-to-dev-map-0 =
< 1708800 762 >;
cpu-to-dev-map-4 =
- < 2035200 762 >,
- < 2092800 2597 >;
+ < 1881600 762 >,
+ < 2208000 2597 >;
};
};
@@ -1164,6 +1170,21 @@
< 1036800000 0x40240936 0x00002b2b 0x3 10 >,
< 1094400000 0x402c0a39 0x00002e2e 0x3 11 >;
+ qcom,l3-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 729600000 0x401c0526 0x00002020 0x1 6 >,
+ < 806400000 0x401c062a 0x00002222 0x1 7 >,
+ < 883200000 0x4024072e 0x00002525 0x2 8 >,
+ < 960000000 0x40240832 0x00002828 0x2 9 >,
+ < 1036800000 0x40240936 0x00002b2b 0x3 10 >,
+ < 1113600000 0x402c0a3a 0x00002e2e 0x3 11 >,
+ < 1209600000 0x402c0b3f 0x00003232 0x3 12 >,
+ < 1305600000 0x40340c44 0x00003636 0x3 13 >;
+
qcom,pwrcl-speedbin0-v0 =
< 300000000 0x000c000f 0x00002020 0x1 1 >,
< 422400000 0x50140116 0x00002020 0x1 2 >,
@@ -1204,6 +1225,27 @@
< 1651200000 0x403c1156 0x00004545 0x3 18 >,
< 1708800000 0x40441259 0x00004747 0x3 19 >;
+ qcom,pwrcl-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 748800000 0x401c0527 0x00002020 0x1 6 >,
+ < 825600000 0x401c062b 0x00002222 0x1 7 >,
+ < 902400000 0x4024072f 0x00002626 0x1 8 >,
+ < 979200000 0x40240833 0x00002929 0x1 9 >,
+ < 1056000000 0x402c0937 0x00002c2c 0x1 10 >,
+ < 1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
+ < 1209600000 0x402c0b3f 0x00003232 0x1 12 >,
+ < 1286400000 0x40340c43 0x00003636 0x2 13 >,
+ < 1363200000 0x40340d47 0x00003939 0x2 14 >,
+ < 1440000000 0x40340e4b 0x00003c3c 0x2 15 >,
+ < 1516800000 0x403c0f4f 0x00003f3f 0x2 16 >,
+ < 1593600000 0x403c1053 0x00004242 0x2 17 >,
+ < 1670400000 0x40441157 0x00004646 0x3 18 >,
+ < 1747200000 0x4044125b 0x00004949 0x3 19 >;
+
qcom,perfcl-speedbin0-v0 =
< 300000000 0x000c000f 0x00002020 0x1 1 >,
< 422400000 0x50140116 0x00002020 0x1 2 >,
@@ -1254,6 +1296,33 @@
< 2035200000 0x404c166a 0x00005555 0x3 23 >,
< 2092800000 0x4054176d 0x00005757 0x3 24 >;
+ qcom,perfcl-speedbin2-v0 =
+ < 300000000 0x000c000f 0x00002020 0x1 1 >,
+ < 422400000 0x50140116 0x00002020 0x1 2 >,
+ < 499200000 0x5014021a 0x00002020 0x1 3 >,
+ < 576000000 0x5014031e 0x00002020 0x1 4 >,
+ < 652800000 0x401c0422 0x00002020 0x1 5 >,
+ < 729600000 0x401c0526 0x00002020 0x1 6 >,
+ < 806400000 0x401c062a 0x00002222 0x1 7 >,
+ < 883200000 0x4024072e 0x00002525 0x1 8 >,
+ < 960000000 0x40240832 0x00002828 0x1 9 >,
+ < 1036800000 0x40240936 0x00002b2b 0x1 10 >,
+ < 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
+ < 1190400000 0x402c0b3e 0x00003232 0x1 12 >,
+ < 1267200000 0x40340c42 0x00003535 0x2 13 >,
+ < 1344000000 0x40340d46 0x00003838 0x2 14 >,
+ < 1420800000 0x40340e4a 0x00003b3b 0x2 15 >,
+ < 1497600000 0x403c0f4e 0x00003e3e 0x2 16 >,
+ < 1574400000 0x403c1052 0x00004242 0x2 17 >,
+ < 1651200000 0x403c1156 0x00004545 0x2 18 >,
+ < 1728000000 0x4044125a 0x00004848 0x3 19 >,
+ < 1804800000 0x4044135e 0x00004b4b 0x3 20 >,
+ < 1881600000 0x404c1462 0x00004e4e 0x3 21 >,
+ < 1958400000 0x404c1566 0x00005252 0x3 22 >,
+ < 2035200000 0x404c166a 0x00005555 0x3 23 >,
+ < 2112000000 0x4054176e 0x00005858 0x3 24 >,
+ < 2208000000 0x40541873 0x00005c5c 0x3 25 >;
+
qcom,l3-min-cpr-vc-bin0 = <7>;
qcom,pwrcl-min-cpr-vc-bin0 = <6>;
qcom,perfcl-min-cpr-vc-bin0 = <7>;
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 4256d9b..b0beb52 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -206,6 +206,7 @@
buf->data = dbuf;
buf->allocated_size = size;
init_completion(&buf->completion);
+ INIT_LIST_HEAD(&buf->list);
#ifdef CONFIG_FW_LOADER_USER_HELPER
INIT_LIST_HEAD(&buf->pending_list);
#endif
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index f184ee1..ff64631 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -583,44 +583,92 @@
static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
bool high_bw_req)
{
+ struct crypto_priv *cp = pengine->pcp;
+ unsigned int control_flag;
int ret = 0;
- if (high_bw_req) {
+ if (cp->ce_support.req_bw_before_clk) {
+ if (high_bw_req)
+ control_flag = QCE_BW_REQUEST_FIRST;
+ else
+ control_flag = QCE_CLK_DISABLE_FIRST;
+ } else {
+ if (high_bw_req)
+ control_flag = QCE_CLK_ENABLE_FIRST;
+ else
+ control_flag = QCE_BW_REQUEST_RESET_FIRST;
+ }
+
+ switch (control_flag) {
+ case QCE_CLK_ENABLE_FIRST:
ret = qce_enable_clk(pengine->qce);
if (ret) {
pr_err("%s Unable enable clk\n", __func__);
- goto clk_err;
+ return;
}
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 1);
if (ret) {
- pr_err("%s Unable to set to high bandwidth\n",
- __func__);
- qce_disable_clk(pengine->qce);
- goto clk_err;
+ pr_err("%s Unable to set high bw\n", __func__);
+ ret = qce_disable_clk(pengine->qce);
+ if (ret)
+ pr_err("%s Unable disable clk\n", __func__);
+ return;
}
- } else {
+ break;
+ case QCE_BW_REQUEST_FIRST:
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 1);
+ if (ret) {
+ pr_err("%s Unable to set high bw\n", __func__);
+ return;
+ }
+ ret = qce_enable_clk(pengine->qce);
+ if (ret) {
+ pr_err("%s Unable enable clk\n", __func__);
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 0);
+ if (ret)
+ pr_err("%s Unable to set low bw\n", __func__);
+ return;
+ }
+ break;
+ case QCE_CLK_DISABLE_FIRST:
+ ret = qce_disable_clk(pengine->qce);
+ if (ret) {
+ pr_err("%s Unable to disable clk\n", __func__);
+ return;
+ }
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 0);
if (ret) {
- pr_err("%s Unable to set to low bandwidth\n",
- __func__);
- goto clk_err;
+ pr_err("%s Unable to set low bw\n", __func__);
+ ret = qce_enable_clk(pengine->qce);
+ if (ret)
+ pr_err("%s Unable enable clk\n", __func__);
+ return;
+ }
+ break;
+ case QCE_BW_REQUEST_RESET_FIRST:
+ ret = msm_bus_scale_client_update_request(
+ pengine->bus_scale_handle, 0);
+ if (ret) {
+ pr_err("%s Unable to set low bw\n", __func__);
+ return;
}
ret = qce_disable_clk(pengine->qce);
if (ret) {
- pr_err("%s Unable disable clk\n", __func__);
+ pr_err("%s Unable to disable clk\n", __func__);
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 1);
if (ret)
- pr_err("%s Unable to set to high bandwidth\n",
- __func__);
- goto clk_err;
+ pr_err("%s Unable to set high bw\n", __func__);
+ return;
}
+ break;
+ default:
+ return;
}
-clk_err:
- return;
-
}
static void qcrypto_bw_reaper_timer_callback(unsigned long data)
@@ -4856,12 +4904,36 @@
if (!pengine)
return -ENOMEM;
- /* open qce */
+ cp->platform_support.bus_scale_table = (struct msm_bus_scale_pdata *)
+ msm_bus_cl_get_pdata(pdev);
+ if (!cp->platform_support.bus_scale_table) {
+ dev_err(&pdev->dev, "bus_scale_table is NULL\n");
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
+ } else {
+ pengine->bus_scale_handle = msm_bus_scale_register_client(
+ (struct msm_bus_scale_pdata *)
+ cp->platform_support.bus_scale_table);
+ if (!pengine->bus_scale_handle) {
+ dev_err(&pdev->dev, "failed to get bus scale handle\n");
+ rc = -ENOMEM;
+ goto exit_kzfree;
+ }
+ pengine->bw_state = BUS_NO_BANDWIDTH;
+ }
+ rc = msm_bus_scale_client_update_request(pengine->bus_scale_handle, 1);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to set high bandwidth\n");
+ goto exit_kzfree;
+ }
handle = qce_open(pdev, &rc);
if (handle == NULL) {
- kzfree(pengine);
- platform_set_drvdata(pdev, NULL);
- return rc;
+ rc = -ENODEV;
+ goto exit_free_pdata;
+ }
+ rc = msm_bus_scale_client_update_request(pengine->bus_scale_handle, 0);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to set low bandwidth\n");
+ goto exit_qce_close;
}
platform_set_drvdata(pdev, pengine);
@@ -4903,7 +4975,7 @@
pengine->max_req, GFP_KERNEL);
if (pqcrypto_req_control == NULL) {
rc = -ENOMEM;
- goto err;
+ goto exit_unlock_mutex;
}
qcrypto_init_req_control(pengine, pqcrypto_req_control);
if (cp->ce_support.bam) {
@@ -4911,15 +4983,7 @@
cp->platform_support.shared_ce_resource = 0;
cp->platform_support.hw_key_support = cp->ce_support.hw_key;
cp->platform_support.sha_hmac = 1;
-
- cp->platform_support.bus_scale_table =
- (struct msm_bus_scale_pdata *)
- msm_bus_cl_get_pdata(pdev);
- if (!cp->platform_support.bus_scale_table)
- pr_warn("bus_scale_table is NULL\n");
-
pengine->ce_device = cp->ce_support.ce_device;
-
} else {
platform_support =
(struct msm_ce_hw_support *)pdev->dev.platform_data;
@@ -4928,33 +4992,11 @@
platform_support->shared_ce_resource;
cp->platform_support.hw_key_support =
platform_support->hw_key_support;
- cp->platform_support.bus_scale_table =
- platform_support->bus_scale_table;
cp->platform_support.sha_hmac = platform_support->sha_hmac;
}
- pengine->bus_scale_handle = 0;
-
- if (cp->platform_support.bus_scale_table != NULL) {
- pengine->bus_scale_handle =
- msm_bus_scale_register_client(
- (struct msm_bus_scale_pdata *)
- cp->platform_support.bus_scale_table);
- if (!pengine->bus_scale_handle) {
- pr_err("%s not able to get bus scale\n",
- __func__);
- rc = -ENOMEM;
- goto err;
- }
- pengine->bw_state = BUS_NO_BANDWIDTH;
- } else {
- pengine->bw_state = BUS_HAS_BANDWIDTH;
- }
-
- if (cp->total_units != 1) {
- mutex_unlock(&cp->engine_lock);
- return 0;
- }
+ if (cp->total_units != 1)
+ goto exit_unlock_mutex;
/* register crypto cipher algorithms the device supports */
for (i = 0; i < ARRAY_SIZE(_qcrypto_ablk_cipher_algos); i++) {
@@ -5243,13 +5285,19 @@
}
mutex_unlock(&cp->engine_lock);
-
return 0;
err:
_qcrypto_remove_engine(pengine);
+ kzfree(pqcrypto_req_control);
+exit_unlock_mutex:
mutex_unlock(&cp->engine_lock);
+exit_qce_close:
if (pengine->qce)
qce_close(pengine->qce);
+exit_free_pdata:
+ msm_bus_scale_client_update_request(pengine->bus_scale_handle, 0);
+ platform_set_drvdata(pdev, NULL);
+exit_kzfree:
kzfree(pengine);
return rc;
};
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 0bc0afb..22a5a8d 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -90,6 +90,14 @@
#define LEVEL_GTF2 2
#define LEVEL_CVT 3
+/*Enum storing luminance types for HDR blocks in EDID*/
+enum luminance_value {
+ NO_LUMINANCE_DATA = 3,
+ MAXIMUM_LUMINANCE = 4,
+ FRAME_AVERAGE_LUMINANCE = 5,
+ MINIMUM_LUMINANCE = 6
+};
+
static const struct edid_quirk {
char vendor[4];
int product_id;
@@ -997,6 +1005,221 @@
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 65 - 1280x720@24Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 66 - 1280x720@25Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+ 3740, 3960, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 67 - 1280x720@30Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 68 - 1280x720@50Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 69 - 1280x720@60Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 70 - 1280x720@100Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 71 - 1280x720@120Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 72 - 1920x1080@24Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 73 - 1920x1080@25Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 74 - 1920x1080@30Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 75 - 1920x1080@50Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 76 - 1920x1080@60Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 77 - 1920x1080@100Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 78 - 1920x1080@120Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 79 - 1680x720@24Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
+ 3080, 3300, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 80 - 1680x720@25Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
+ 2948, 3168, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 81 - 1680x720@30Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
+ 2420, 2640, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 82 - 1680x720@50Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 83 - 1680x720@60Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
+ 1980, 2200, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 84 - 1680x720@100Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 85 - 1680x720@120Hz */
+ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
+ 1780, 2000, 0, 720, 725, 730, 825, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 86 - 2560x1080@24Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
+ 3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 87 - 2560x1080@25Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
+ 3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 88 - 2560x1080@30Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
+ 3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 89 - 2560x1080@50Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 90 - 2560x1080@60Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
+ 2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 91 - 2560x1080@100Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
+ 2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 92 - 2560x1080@120Hz */
+ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
+ 3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+ /* 93 - 3840x2160p@24Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,},
+ /* 94 - 3840x2160p@25Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 95 - 3840x2160p@30Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 96 - 3840x2160p@50Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 97 - 3840x2160p@60Hz 16:9 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+ /* 98 - 4096x2160p@24Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 99 - 4096x2160p@25Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 100 - 4096x2160p@30Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 101 - 4096x2160p@50Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
+ 5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 102 - 4096x2160p@60Hz 256:135 */
+ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
+ 4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+ /* 103 - 3840x2160p@24Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+ 5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 104 - 3840x2160p@25Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 105 - 3840x2160p@30Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 106 - 3840x2160p@50Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+ 4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+ /* 107 - 3840x2160p@60Hz 64:27 */
+ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+ 4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
};
/*
@@ -2514,12 +2737,15 @@
return closure.modes;
}
-
+#define VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK 0x0
#define AUDIO_BLOCK 0x01
#define VIDEO_BLOCK 0x02
#define VENDOR_BLOCK 0x03
#define SPEAKER_BLOCK 0x04
+#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06
+#define EXTENDED_TAG 0x07
#define VIDEO_CAPABILITY_BLOCK 0x07
+#define Y420_VIDEO_DATA_BLOCK 0x0E
#define EDID_BASIC_AUDIO (1 << 6)
#define EDID_CEA_YCRCB444 (1 << 5)
#define EDID_CEA_YCRCB422 (1 << 4)
@@ -3168,6 +3394,21 @@
return hdmi_id == HDMI_IEEE_OUI;
}
+static bool cea_db_is_hdmi_hf_vsdb(const u8 *db)
+{
+ int hdmi_id;
+
+ if (cea_db_tag(db) != VENDOR_BLOCK)
+ return false;
+
+ if (cea_db_payload_len(db) < 7)
+ return false;
+
+ hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
+
+ return hdmi_id == HDMI_IEEE_OUI_HF;
+}
+
#define for_each_cea_db(cea, i, start, end) \
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
@@ -3287,6 +3528,227 @@
}
static void
+parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db)
+{
+ u8 len = cea_db_payload_len(db);
+
+ if (len < 7)
+ return;
+
+ if (db[4] != 1)
+ return; /* invalid version */
+
+ connector->max_tmds_char = db[5] * 5;
+ connector->scdc_present = db[6] & (1 << 7);
+ connector->rr_capable = db[6] & (1 << 6);
+ connector->flags_3d = db[6] & 0x7;
+ connector->supports_scramble = connector->scdc_present &&
+ (db[6] & (1 << 3));
+
+ DRM_DEBUG_KMS("HDMI v2: max TMDS char %d, "
+ "scdc %s, "
+ "rr %s, "
+ "3D flags 0x%x, "
+ "scramble %s\n",
+ connector->max_tmds_char,
+ connector->scdc_present ? "available" : "not available",
+ connector->rr_capable ? "capable" : "not capable",
+ connector->flags_3d,
+ connector->supports_scramble ?
+ "supported" : "not supported");
+}
+
+/*
+ * drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the CEA vendor specific block
+ *
+ * Parses the HDMI VCDB to extract sink info for @connector.
+ */
+static void
+drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db)
+{
+ /*
+ * Check if the sink specifies underscan
+ * support for:
+ * BIT 5: preferred video format
+ * BIT 3: IT video format
+ * BIT 1: CE video format
+ */
+
+ connector->pt_scan_info =
+ (db[2] & (BIT(4) | BIT(5))) >> 4;
+ connector->it_scan_info =
+ (db[2] & (BIT(3) | BIT(2))) >> 2;
+ connector->ce_scan_info =
+ db[2] & (BIT(1) | BIT(0));
+
+ DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)",
+ (int) connector->pt_scan_info,
+ (int) connector->it_scan_info,
+ (int) connector->ce_scan_info);
+}
+
+static bool drm_edid_is_luminance_value_present(
+u32 block_length, enum luminance_value value)
+{
+ return block_length > NO_LUMINANCE_DATA && value <= block_length;
+}
+
+/*
+ * drm_extract_hdr_db - Parse the HDMI HDR extended block
+ * @connector: connector corresponding to the HDMI sink
+ * @db: start of the HDMI HDR extended block
+ *
+ * Parses the HDMI HDR extended block to extract sink info for @connector.
+ */
+static void
+drm_extract_hdr_db(struct drm_connector *connector, const u8 *db)
+{
+
+ u8 len = 0;
+
+ if (!db)
+ return;
+
+ len = db[0] & 0x1f;
+ /* Byte 3: Electro-Optical Transfer Functions */
+ connector->hdr_eotf = db[2] & 0x3F;
+
+ /* Byte 4: Static Metadata Descriptor Type 1 */
+ connector->hdr_metadata_type_one = (db[3] & BIT(0));
+
+ /* Byte 5: Desired Content Maximum Luminance */
+ if (drm_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE))
+ connector->hdr_max_luminance =
+ db[MAXIMUM_LUMINANCE];
+
+ /* Byte 6: Desired Content Max Frame-average Luminance */
+ if (drm_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE))
+ connector->hdr_avg_luminance =
+ db[FRAME_AVERAGE_LUMINANCE];
+
+ /* Byte 7: Desired Content Min Luminance */
+ if (drm_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE))
+ connector->hdr_min_luminance =
+ db[MINIMUM_LUMINANCE];
+
+ connector->hdr_supported = true;
+
+ DRM_DEBUG_KMS("HDR electro-optical %d\n", connector->hdr_eotf);
+ DRM_DEBUG_KMS("metadata desc 1 %d\n", connector->hdr_metadata_type_one);
+ DRM_DEBUG_KMS("max luminance %d\n", connector->hdr_max_luminance);
+ DRM_DEBUG_KMS("avg luminance %d\n", connector->hdr_avg_luminance);
+ DRM_DEBUG_KMS("min luminance %d\n", connector->hdr_min_luminance);
+}
+
+/*
+ * drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the all extended tag blocks extract sink info for @connector.
+ */
+static void
+drm_hdmi_extract_extended_blk_info(struct drm_connector *connector,
+struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == EXTENDED_TAG) {
+ DRM_DEBUG_KMS("found extended tag block = %d\n",
+ db[1]);
+ switch (db[1]) {
+ case VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK:
+ drm_extract_vcdb_info(connector, db);
+ break;
+ case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK:
+ drm_extract_hdr_db(connector, db);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+static u8 *
+drm_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid)
+ return NULL;
+
+ cea = drm_find_cea_extension(edid);
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((cea_db_tag(db) == EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * add_YCbCr420VDB_modes - add the modes found in Ycbcr420 VDB block
+ * @connector: connector corresponding to the HDMI sink
+ * @edid: handle to the EDID structure
+ * Parses the YCbCr420 VDB block and adds the modes to @connector.
+ */
+static int
+add_YCbCr420VDB_modes(struct drm_connector *connector, struct edid *edid)
+{
+
+ const u8 *db = NULL;
+ u32 i = 0;
+ u32 modes = 0;
+ u32 video_format = 0;
+ u8 len = 0;
+
+ /*Find the YCbCr420 VDB*/
+ db = drm_edid_find_extended_tag_block(edid, Y420_VIDEO_DATA_BLOCK);
+ /* Offset to byte 3 */
+ if (db) {
+ len = db[0] & 0x1F;
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ struct drm_display_mode *mode;
+
+ video_format = *(db + i) & 0x7F;
+ mode = drm_display_mode_from_vic_index(connector,
+ db, len-1, i);
+ if (mode) {
+ DRM_DEBUG_KMS("Adding mode for vic = %d\n",
+ video_format);
+ drm_mode_probed_add(connector, mode);
+ modes++;
+ }
+ }
+ }
+ return modes;
+}
+
+static void
monitor_name(struct detailed_timing *t, void *data)
{
if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
@@ -3410,6 +3872,9 @@
/* HDMI Vendor-Specific Data Block */
if (cea_db_is_hdmi_vsdb(db))
drm_parse_hdmi_vsdb_audio(connector, db);
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
break;
default:
break;
@@ -3840,6 +4305,37 @@
}
}
+static void
+drm_hdmi_extract_vsdbs_info(struct drm_connector *connector, struct edid *edid)
+{
+ const u8 *cea = drm_find_cea_extension(edid);
+ const u8 *db = NULL;
+
+ if (cea && cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (cea_db_offsets(cea, &start, &end))
+ return;
+
+ for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+
+ if (cea_db_tag(db) == VENDOR_BLOCK) {
+ /* HDMI Vendor-Specific Data Block */
+ if (cea_db_is_hdmi_vsdb(db)) {
+ drm_parse_hdmi_vsdb_video(
+ connector, db);
+ drm_parse_hdmi_vsdb_audio(
+ connector, db);
+ }
+ /* HDMI Forum Vendor-Specific Data Block */
+ else if (cea_db_is_hdmi_hf_vsdb(db))
+ parse_hdmi_hf_vsdb(connector, db);
+ }
+ }
+ }
+}
+
static void drm_add_display_info(struct drm_connector *connector,
struct edid *edid)
{
@@ -3877,6 +4373,11 @@
connector->name, info->bpc);
}
+ /* Extract audio and video latency fields for the sink */
+ drm_hdmi_extract_vsdbs_info(connector, edid);
+ /* Extract info from extended tag blocks */
+ drm_hdmi_extract_extended_blk_info(connector, edid);
+
/* Only defined for 1.4 with digital displays */
if (edid->revision < 4)
return;
@@ -4091,6 +4592,7 @@
num_modes += add_cea_modes(connector, edid);
num_modes += add_alternate_cea_modes(connector, edid);
num_modes += add_displayid_detailed_modes(connector, edid);
+ num_modes += add_YCbCr420VDB_modes(connector, edid);
if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
num_modes += add_inferred_modes(connector, edid);
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4112bef..651c0576 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -51,6 +51,7 @@
sde/sde_hw_reg_dma_v1_color_proc.o \
sde/sde_hw_color_proc_v4.o \
sde/sde_hw_ad4.o \
+ sde_edid_parser.o
msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
sde_rsc_hw.o \
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
new file mode 100644
index 0000000..7102d8a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -0,0 +1,1396 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include "dp_ctrl.h"
+
+#define DP_LINK_RATE_MULTIPLIER 27000000
+#define DP_KHZ_TO_HZ 1000
+#define DP_CRYPTO_CLK_RATE_KHZ 180000
+
+/* sink power state */
+#define SINK_POWER_ON 1
+#define SINK_POWER_OFF 2
+
+#define DP_CTRL_INTR_READY_FOR_VIDEO BIT(0)
+#define DP_CTRL_INTR_IDLE_PATTERN_SENT BIT(3)
+
+/* dp state ctrl */
+#define ST_TRAIN_PATTERN_1 BIT(0)
+#define ST_TRAIN_PATTERN_2 BIT(1)
+#define ST_TRAIN_PATTERN_3 BIT(2)
+#define ST_TRAIN_PATTERN_4 BIT(3)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(4)
+#define ST_PRBS7 BIT(5)
+#define ST_CUSTOM_80_BIT_PATTERN BIT(6)
+#define ST_SEND_VIDEO BIT(7)
+#define ST_PUSH_IDLE BIT(8)
+
+struct dp_vc_tu_mapping_table {
+ u32 vic;
+ u8 lanes;
+ u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
+ u8 bpp;
+ u8 valid_boundary_link;
+ u16 delay_start_link;
+ bool boundary_moderation_en;
+ u8 valid_lower_boundary_link;
+ u8 upper_boundary_count;
+ u8 lower_boundary_count;
+ u8 tu_size_minus1;
+};
+
+struct dp_ctrl_private {
+ struct dp_ctrl dp_ctrl;
+
+ struct device *dev;
+ struct dp_aux *aux;
+ struct dp_panel *panel;
+ struct dp_link *link;
+ struct dp_power *power;
+ struct dp_parser *parser;
+ struct dp_catalog_ctrl *catalog;
+
+ struct completion idle_comp;
+ struct completion video_comp;
+ struct completion irq_comp;
+
+ bool hpd_irq_on;
+ bool power_on;
+ bool sink_info_read;
+ bool cont_splash;
+ bool psm_enabled;
+ bool initialized;
+ bool orientation;
+
+ u32 pixel_rate;
+ u32 vic;
+};
+
+enum notification_status {
+ NOTIFY_UNKNOWN,
+ NOTIFY_CONNECT,
+ NOTIFY_DISCONNECT,
+ NOTIFY_CONNECT_IRQ_HPD,
+ NOTIFY_DISCONNECT_IRQ_HPD,
+};
+
+static void dp_ctrl_idle_patterns_sent(struct dp_ctrl_private *ctrl)
+{
+ pr_debug("idle_patterns_sent\n");
+ complete(&ctrl->idle_comp);
+}
+
+static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl)
+{
+ pr_debug("dp_video_ready\n");
+ complete(&ctrl->video_comp);
+}
+
+static void dp_ctrl_set_sink_power_state(struct dp_ctrl_private *ctrl,
+ u8 power_state)
+{
+ const int len = 1;
+
+ ctrl->aux->write(ctrl->aux, 0x600, len, AUX_NATIVE, &power_state);
+}
+
+static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state)
+{
+ ctrl->catalog->state_ctrl(ctrl->catalog, state);
+}
+
+static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
+{
+ int const idle_pattern_completion_timeout_ms = 3 * HZ / 100;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ dp_ctrl_set_sink_power_state(ctrl, SINK_POWER_OFF);
+
+ reinit_completion(&ctrl->idle_comp);
+ dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
+
+ if (!wait_for_completion_timeout(&ctrl->idle_comp,
+ idle_pattern_completion_timeout_ms))
+ pr_warn("PUSH_IDLE pattern timedout\n");
+
+ pr_debug("mainlink off done\n");
+}
+
+static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
+{
+ u32 config = 0, tbd;
+
+ config |= (2 << 13); /* Default-> LSCLK DIV: 1/4 LCLK */
+ config |= (0 << 11); /* RGB */
+
+ /* Scrambler reset enable */
+ if (ctrl->panel->dpcd.scrambler_reset)
+ config |= (1 << 10);
+
+ tbd = ctrl->link->get_test_bits_depth(ctrl->link,
+ ctrl->panel->pinfo.bpp);
+ config |= tbd << 8;
+
+ /* Num of Lanes */
+ config |= ((ctrl->link->lane_count - 1) << 4);
+
+ if (ctrl->panel->dpcd.enhanced_frame)
+ config |= 0x40;
+
+ config |= 0x04; /* progressive video */
+
+ config |= 0x03; /* sycn clock & static Mvid */
+
+ ctrl->catalog->config_ctrl(ctrl->catalog, config);
+}
+
+/**
+ * dp_ctrl_configure_source_params() - configures DP transmitter source params
+ * @ctrl: Display Port Driver data
+ *
+ * Configures the DP transmitter source params including details such as lane
+ * configuration, output format and sink/panel timing information.
+ */
+static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
+{
+ u32 cc, tb;
+
+ ctrl->catalog->lane_mapping(ctrl->catalog);
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+ dp_ctrl_config_ctrl(ctrl);
+
+ tb = ctrl->link->get_test_bits_depth(ctrl->link,
+ ctrl->panel->pinfo.bpp);
+ cc = ctrl->link->get_colorimetry_config(ctrl->link);
+ ctrl->catalog->config_misc(ctrl->catalog, cc, tb);
+
+ ctrl->catalog->config_msa(ctrl->catalog);
+
+ ctrl->panel->timing_cfg(ctrl->panel);
+}
+
+static void dp_ctrl_get_extra_req_bytes(u64 result_valid,
+ int valid_bdary_link,
+ u64 value1, u64 value2,
+ bool *negative, u64 *result,
+ u64 compare)
+{
+ *negative = false;
+ if (result_valid >= compare) {
+ if (valid_bdary_link
+ >= compare)
+ *result = value1 + value2;
+ else {
+ if (value1 < value2)
+ *negative = true;
+ *result = (value1 >= value2) ?
+ (value1 - value2) : (value2 - value1);
+ }
+ } else {
+ if (valid_bdary_link
+ >= compare) {
+ if (value1 >= value2)
+ *negative = true;
+ *result = (value1 >= value2) ?
+ (value1 - value2) : (value2 - value1);
+ } else {
+ *result = value1 + value2;
+ *negative = true;
+ }
+ }
+}
+
+static u64 roundup_u64(u64 x, u64 y)
+{
+ x += (y - 1);
+ return (div64_ul(x, y) * y);
+}
+
+static u64 rounddown_u64(u64 x, u64 y)
+{
+ u64 rem;
+
+ div64_u64_rem(x, y, &rem);
+ return (x - rem);
+}
+
+static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
+ struct dp_vc_tu_mapping_table *tu_table)
+{
+ u32 const multiplier = 1000000;
+ u64 pclk, lclk;
+ u8 bpp, ln_cnt, link_rate;
+ int run_idx = 0;
+ u32 lwidth, h_blank;
+ u32 fifo_empty = 0;
+ u32 ratio_scale = 1001;
+ u64 temp, ratio, original_ratio;
+ u64 temp2, reminder;
+ u64 temp3, temp4, result = 0;
+
+ u64 err = multiplier;
+ u64 n_err = 0, n_n_err = 0;
+ bool n_err_neg, nn_err_neg;
+ u8 hblank_margin = 16;
+
+ u8 tu_size, tu_size_desired = 0, tu_size_minus1;
+ int valid_boundary_link;
+ u64 resulting_valid;
+ u64 total_valid;
+ u64 effective_valid;
+ u64 effective_valid_recorded;
+ int n_tus;
+ int n_tus_per_lane;
+ int paired_tus;
+ int remainder_tus;
+ int remainder_tus_upper, remainder_tus_lower;
+ int extra_bytes;
+ int filler_size;
+ int delay_start_link;
+ int boundary_moderation_en = 0;
+ int upper_bdry_cnt = 0;
+ int lower_bdry_cnt = 0;
+ int i_upper_bdry_cnt = 0;
+ int i_lower_bdry_cnt = 0;
+ int valid_lower_boundary_link = 0;
+ int even_distribution_bf = 0;
+ int even_distribution_legacy = 0;
+ int even_distribution = 0;
+ int min_hblank = 0;
+ int extra_pclk_cycles;
+ u8 extra_pclk_cycle_delay = 4;
+ int extra_pclk_cycles_in_link_clk;
+ u64 ratio_by_tu;
+ u64 average_valid2;
+ u64 extra_buffer_margin;
+ int new_valid_boundary_link;
+
+ u64 resulting_valid_tmp;
+ u64 ratio_by_tu_tmp;
+ int n_tus_tmp;
+ int extra_pclk_cycles_tmp;
+ int extra_pclk_cycles_in_lclk_tmp;
+ int extra_req_bytes_new_tmp;
+ int filler_size_tmp;
+ int lower_filler_size_tmp;
+ int delay_start_link_tmp;
+ int min_hblank_tmp = 0;
+ bool extra_req_bytes_is_neg = false;
+ struct dp_panel_info *pinfo = &ctrl->panel->pinfo;
+
+ u8 dp_brute_force = 1;
+ u64 brute_force_threshold = 10;
+ u64 diff_abs;
+
+ link_rate = ctrl->link->link_rate;
+ ln_cnt = ctrl->link->lane_count;
+
+ bpp = pinfo->bpp;
+ lwidth = pinfo->h_active;
+ h_blank = pinfo->h_back_porch + pinfo->h_front_porch +
+ pinfo->h_sync_width;
+ pclk = pinfo->pixel_clk_khz * 1000;
+
+ boundary_moderation_en = 0;
+ upper_bdry_cnt = 0;
+ lower_bdry_cnt = 0;
+ i_upper_bdry_cnt = 0;
+ i_lower_bdry_cnt = 0;
+ valid_lower_boundary_link = 0;
+ even_distribution_bf = 0;
+ even_distribution_legacy = 0;
+ even_distribution = 0;
+ min_hblank = 0;
+
+ lclk = link_rate * DP_LINK_RATE_MULTIPLIER;
+
+ pr_debug("pclk=%lld, active_width=%d, h_blank=%d\n",
+ pclk, lwidth, h_blank);
+ pr_debug("lclk = %lld, ln_cnt = %d\n", lclk, ln_cnt);
+ ratio = div64_u64_rem(pclk * bpp * multiplier,
+ 8 * ln_cnt * lclk, &reminder);
+ ratio = div64_u64((pclk * bpp * multiplier), (8 * ln_cnt * lclk));
+ original_ratio = ratio;
+
+ extra_buffer_margin = roundup_u64(div64_u64(extra_pclk_cycle_delay
+ * lclk * multiplier, pclk), multiplier);
+ extra_buffer_margin = div64_u64(extra_buffer_margin, multiplier);
+
+ /* To deal with cases where lines are not distributable */
+ if (((lwidth % ln_cnt) != 0) && ratio < multiplier) {
+ ratio = ratio * ratio_scale;
+ ratio = ratio < (1000 * multiplier)
+ ? ratio : (1000 * multiplier);
+ }
+ pr_debug("ratio = %lld\n", ratio);
+
+ for (tu_size = 32; tu_size <= 64; tu_size++) {
+ temp = ratio * tu_size;
+ temp2 = ((temp / multiplier) + 1) * multiplier;
+ n_err = roundup_u64(temp, multiplier) - temp;
+
+ if (n_err < err) {
+ err = n_err;
+ tu_size_desired = tu_size;
+ }
+ }
+ pr_debug("Info: tu_size_desired = %d\n", tu_size_desired);
+
+ tu_size_minus1 = tu_size_desired - 1;
+
+ valid_boundary_link = roundup_u64(ratio * tu_size_desired, multiplier);
+ valid_boundary_link /= multiplier;
+ n_tus = rounddown((lwidth * bpp * multiplier)
+ / (8 * valid_boundary_link), multiplier) / multiplier;
+ even_distribution_legacy = n_tus % ln_cnt == 0 ? 1 : 0;
+ pr_debug("Info: n_symbol_per_tu=%d, number_of_tus=%d\n",
+ valid_boundary_link, n_tus);
+
+ extra_bytes = roundup_u64((n_tus + 1)
+ * ((valid_boundary_link * multiplier)
+ - (original_ratio * tu_size_desired)), multiplier);
+ extra_bytes /= multiplier;
+ extra_pclk_cycles = roundup(extra_bytes * 8 * multiplier / bpp,
+ multiplier);
+ extra_pclk_cycles /= multiplier;
+ extra_pclk_cycles_in_link_clk = roundup_u64(div64_u64(extra_pclk_cycles
+ * lclk * multiplier, pclk), multiplier);
+ extra_pclk_cycles_in_link_clk /= multiplier;
+ filler_size = roundup_u64((tu_size_desired - valid_boundary_link)
+ * multiplier, multiplier);
+ filler_size /= multiplier;
+ ratio_by_tu = div64_u64(ratio * tu_size_desired, multiplier);
+
+ pr_debug("extra_pclk_cycles_in_link_clk=%d, extra_bytes=%d\n",
+ extra_pclk_cycles_in_link_clk, extra_bytes);
+ pr_debug("extra_pclk_cycles_in_link_clk=%d\n",
+ extra_pclk_cycles_in_link_clk);
+ pr_debug("filler_size=%d, extra_buffer_margin=%lld\n",
+ filler_size, extra_buffer_margin);
+
+ delay_start_link = ((extra_bytes > extra_pclk_cycles_in_link_clk)
+ ? extra_bytes
+ : extra_pclk_cycles_in_link_clk)
+ + filler_size + extra_buffer_margin;
+ resulting_valid = valid_boundary_link;
+ pr_debug("Info: delay_start_link=%d, filler_size=%d\n",
+ delay_start_link, filler_size);
+ pr_debug("valid_boundary_link=%d ratio_by_tu=%lld\n",
+ valid_boundary_link, ratio_by_tu);
+
+ diff_abs = (resulting_valid >= ratio_by_tu)
+ ? (resulting_valid - ratio_by_tu)
+ : (ratio_by_tu - resulting_valid);
+
+ if (err != 0 && ((diff_abs > brute_force_threshold)
+ || (even_distribution_legacy == 0)
+ || (dp_brute_force == 1))) {
+ err = multiplier;
+ for (tu_size = 32; tu_size <= 64; tu_size++) {
+ for (i_upper_bdry_cnt = 1; i_upper_bdry_cnt <= 15;
+ i_upper_bdry_cnt++) {
+ for (i_lower_bdry_cnt = 1;
+ i_lower_bdry_cnt <= 15;
+ i_lower_bdry_cnt++) {
+ new_valid_boundary_link =
+ roundup_u64(ratio
+ * tu_size, multiplier);
+ average_valid2 = (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier))
+ / (i_upper_bdry_cnt
+ + i_lower_bdry_cnt);
+ n_tus = rounddown_u64(div64_u64(lwidth
+ * multiplier * multiplier
+ * (bpp / 8), average_valid2),
+ multiplier);
+ n_tus /= multiplier;
+ n_tus_per_lane
+ = rounddown(n_tus
+ * multiplier
+ / ln_cnt, multiplier);
+ n_tus_per_lane /= multiplier;
+ paired_tus =
+ rounddown((n_tus_per_lane)
+ * multiplier
+ / (i_upper_bdry_cnt
+ + i_lower_bdry_cnt),
+ multiplier);
+ paired_tus /= multiplier;
+ remainder_tus = n_tus_per_lane
+ - paired_tus
+ * (i_upper_bdry_cnt
+ + i_lower_bdry_cnt);
+ if ((remainder_tus
+ - i_upper_bdry_cnt) > 0) {
+ remainder_tus_upper
+ = i_upper_bdry_cnt;
+ remainder_tus_lower =
+ remainder_tus
+ - i_upper_bdry_cnt;
+ } else {
+ remainder_tus_upper
+ = remainder_tus;
+ remainder_tus_lower = 0;
+ }
+ total_valid = paired_tus
+ * (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier))
+ + (remainder_tus_upper
+ * new_valid_boundary_link)
+ + (remainder_tus_lower
+ * (new_valid_boundary_link
+ - multiplier));
+ n_err_neg = nn_err_neg = false;
+ effective_valid
+ = div_u64(total_valid,
+ n_tus_per_lane);
+ n_n_err = (effective_valid
+ >= (ratio * tu_size))
+ ? (effective_valid
+ - (ratio * tu_size))
+ : ((ratio * tu_size)
+ - effective_valid);
+ if (effective_valid < (ratio * tu_size))
+ nn_err_neg = true;
+ n_err = (average_valid2
+ >= (ratio * tu_size))
+ ? (average_valid2
+ - (ratio * tu_size))
+ : ((ratio * tu_size)
+ - average_valid2);
+ if (average_valid2 < (ratio * tu_size))
+ n_err_neg = true;
+ even_distribution =
+ n_tus % ln_cnt == 0 ? 1 : 0;
+ diff_abs =
+ resulting_valid >= ratio_by_tu
+ ? (resulting_valid
+ - ratio_by_tu)
+ : (ratio_by_tu
+ - resulting_valid);
+
+ resulting_valid_tmp = div64_u64(
+ (i_upper_bdry_cnt
+ * new_valid_boundary_link
+ + i_lower_bdry_cnt
+ * (new_valid_boundary_link
+ - multiplier)),
+ (i_upper_bdry_cnt
+ + i_lower_bdry_cnt));
+ ratio_by_tu_tmp =
+ original_ratio * tu_size;
+ ratio_by_tu_tmp /= multiplier;
+ n_tus_tmp = rounddown_u64(
+ div64_u64(lwidth
+ * multiplier * multiplier
+ * bpp / 8,
+ resulting_valid_tmp),
+ multiplier);
+ n_tus_tmp /= multiplier;
+
+ temp3 = (resulting_valid_tmp
+ >= (original_ratio * tu_size))
+ ? (resulting_valid_tmp
+ - original_ratio * tu_size)
+ : (original_ratio * tu_size)
+ - resulting_valid_tmp;
+ temp3 = (n_tus_tmp + 1) * temp3;
+ temp4 = (new_valid_boundary_link
+ >= (original_ratio * tu_size))
+ ? (new_valid_boundary_link
+ - original_ratio
+ * tu_size)
+ : (original_ratio * tu_size)
+ - new_valid_boundary_link;
+ temp4 = (i_upper_bdry_cnt
+ * ln_cnt * temp4);
+
+ temp3 = roundup_u64(temp3, multiplier);
+ temp4 = roundup_u64(temp4, multiplier);
+ dp_ctrl_get_extra_req_bytes
+ (resulting_valid_tmp,
+ new_valid_boundary_link,
+ temp3, temp4,
+ &extra_req_bytes_is_neg,
+ &result,
+ (original_ratio * tu_size));
+ extra_req_bytes_new_tmp
+ = div64_ul(result, multiplier);
+ if ((extra_req_bytes_is_neg)
+ && (extra_req_bytes_new_tmp
+ > 1))
+ extra_req_bytes_new_tmp
+ = extra_req_bytes_new_tmp - 1;
+ if (extra_req_bytes_new_tmp == 0)
+ extra_req_bytes_new_tmp = 1;
+ extra_pclk_cycles_tmp =
+ (u64)(extra_req_bytes_new_tmp
+ * 8 * multiplier) / bpp;
+ extra_pclk_cycles_tmp /= multiplier;
+
+ if (extra_pclk_cycles_tmp <= 0)
+ extra_pclk_cycles_tmp = 1;
+ extra_pclk_cycles_in_lclk_tmp =
+ roundup_u64(div64_u64(
+ extra_pclk_cycles_tmp
+ * lclk * multiplier,
+ pclk), multiplier);
+ extra_pclk_cycles_in_lclk_tmp
+ /= multiplier;
+ filler_size_tmp = roundup_u64(
+ (tu_size * multiplier *
+ new_valid_boundary_link),
+ multiplier);
+ filler_size_tmp /= multiplier;
+ lower_filler_size_tmp =
+ filler_size_tmp + 1;
+ if (extra_req_bytes_is_neg)
+ temp3 = (extra_req_bytes_new_tmp
+ > extra_pclk_cycles_in_lclk_tmp
+ ? extra_pclk_cycles_in_lclk_tmp
+ : extra_req_bytes_new_tmp);
+ else
+ temp3 = (extra_req_bytes_new_tmp
+ > extra_pclk_cycles_in_lclk_tmp
+ ? extra_req_bytes_new_tmp :
+ extra_pclk_cycles_in_lclk_tmp);
+
+ temp4 = lower_filler_size_tmp
+ + extra_buffer_margin;
+ if (extra_req_bytes_is_neg)
+ delay_start_link_tmp
+ = (temp3 >= temp4)
+ ? (temp3 - temp4)
+ : (temp4 - temp3);
+ else
+ delay_start_link_tmp
+ = temp3 + temp4;
+
+ min_hblank_tmp = (int)div64_u64(
+ roundup_u64(
+ div64_u64(delay_start_link_tmp
+ * pclk * multiplier, lclk),
+ multiplier), multiplier)
+ + hblank_margin;
+
+ if (((even_distribution == 1)
+ || ((even_distribution_bf == 0)
+ && (even_distribution_legacy
+ == 0)))
+ && !n_err_neg && !nn_err_neg
+ && n_n_err < err
+ && (n_n_err < diff_abs
+ || (dp_brute_force == 1))
+ && (new_valid_boundary_link
+ - 1) > 0
+ && (h_blank >=
+ (u32)min_hblank_tmp)) {
+ upper_bdry_cnt =
+ i_upper_bdry_cnt;
+ lower_bdry_cnt =
+ i_lower_bdry_cnt;
+ err = n_n_err;
+ boundary_moderation_en = 1;
+ tu_size_desired = tu_size;
+ valid_boundary_link =
+ new_valid_boundary_link;
+ effective_valid_recorded
+ = effective_valid;
+ delay_start_link
+ = delay_start_link_tmp;
+ filler_size = filler_size_tmp;
+ min_hblank = min_hblank_tmp;
+ n_tus = n_tus_tmp;
+ even_distribution_bf = 1;
+
+ pr_debug("upper_bdry_cnt=%d, lower_boundary_cnt=%d, err=%lld, tu_size_desired=%d, valid_boundary_link=%d, effective_valid=%lld\n",
+ upper_bdry_cnt,
+ lower_bdry_cnt, err,
+ tu_size_desired,
+ valid_boundary_link,
+ effective_valid);
+ }
+ }
+ }
+ }
+
+ if (boundary_moderation_en == 1) {
+ resulting_valid = (u64)(upper_bdry_cnt
+ *valid_boundary_link + lower_bdry_cnt
+ * (valid_boundary_link - 1))
+ / (upper_bdry_cnt + lower_bdry_cnt);
+ ratio_by_tu = original_ratio * tu_size_desired;
+ valid_lower_boundary_link =
+ (valid_boundary_link / multiplier) - 1;
+
+ tu_size_minus1 = tu_size_desired - 1;
+ even_distribution_bf = 1;
+ valid_boundary_link /= multiplier;
+ pr_debug("Info: Boundary_moderation enabled\n");
+ }
+ }
+
+ min_hblank = ((int) roundup_u64(div64_u64(delay_start_link * pclk
+ * multiplier, lclk), multiplier))
+ / multiplier + hblank_margin;
+ if (h_blank < (u32)min_hblank) {
+ pr_debug(" WARNING: run_idx=%d Programmed h_blank %d is smaller than the min_hblank %d supported.\n",
+ run_idx, h_blank, min_hblank);
+ }
+
+ if (fifo_empty) {
+ tu_size_minus1 = 31;
+ valid_boundary_link = 32;
+ delay_start_link = 0;
+ boundary_moderation_en = 0;
+ }
+
+ pr_debug("tu_size_minus1=%d valid_boundary_link=%d delay_start_link=%d boundary_moderation_en=%d\n upper_boundary_cnt=%d lower_boundary_cnt=%d valid_lower_boundary_link=%d min_hblank=%d\n",
+ tu_size_minus1, valid_boundary_link, delay_start_link,
+ boundary_moderation_en, upper_bdry_cnt, lower_bdry_cnt,
+ valid_lower_boundary_link, min_hblank);
+
+ tu_table->valid_boundary_link = valid_boundary_link;
+ tu_table->delay_start_link = delay_start_link;
+ tu_table->boundary_moderation_en = boundary_moderation_en;
+ tu_table->valid_lower_boundary_link = valid_lower_boundary_link;
+ tu_table->upper_boundary_count = upper_bdry_cnt;
+ tu_table->lower_boundary_count = lower_bdry_cnt;
+ tu_table->tu_size_minus1 = tu_size_minus1;
+}
+
+static void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
+{
+ u32 dp_tu = 0x0;
+ u32 valid_boundary = 0x0;
+ u32 valid_boundary2 = 0x0;
+ struct dp_vc_tu_mapping_table tu_calc_table;
+
+ dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
+
+ dp_tu |= tu_calc_table.tu_size_minus1;
+ valid_boundary |= tu_calc_table.valid_boundary_link;
+ valid_boundary |= (tu_calc_table.delay_start_link << 16);
+
+ valid_boundary2 |= (tu_calc_table.valid_lower_boundary_link << 1);
+ valid_boundary2 |= (tu_calc_table.upper_boundary_count << 16);
+ valid_boundary2 |= (tu_calc_table.lower_boundary_count << 20);
+
+ if (tu_calc_table.boundary_moderation_en)
+ valid_boundary2 |= BIT(0);
+
+ pr_debug("dp_tu=0x%x, valid_boundary=0x%x, valid_boundary2=0x%x\n",
+ dp_tu, valid_boundary, valid_boundary2);
+
+ ctrl->catalog->dp_tu = dp_tu;
+ ctrl->catalog->valid_boundary = valid_boundary;
+ ctrl->catalog->valid_boundary2 = valid_boundary2;
+
+ ctrl->catalog->update_transfer_unit(ctrl->catalog);
+}
+
+static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (ctrl->cont_splash)
+ return ret;
+
+ ret = wait_for_completion_timeout(&ctrl->video_comp, HZ / 2);
+ if (ret <= 0) {
+ pr_err("Link Train timedout\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl,
+ u32 voltage_level, u32 pre_emphasis_level)
+{
+ int i;
+ u8 buf[4];
+ u32 max_level_reached = 0;
+
+ if (voltage_level == DP_LINK_VOLTAGE_MAX) {
+ pr_debug("max. voltage swing level reached %d\n",
+ voltage_level);
+ max_level_reached |= BIT(2);
+ }
+
+ if (pre_emphasis_level == DP_LINK_PRE_EMPHASIS_MAX) {
+ pr_debug("max. pre-emphasis level reached %d\n",
+ pre_emphasis_level);
+ max_level_reached |= BIT(5);
+ }
+
+ pr_debug("max_level_reached = 0x%x\n", max_level_reached);
+
+ pre_emphasis_level <<= 3;
+
+ for (i = 0; i < 4; i++)
+ buf[i] = voltage_level | pre_emphasis_level | max_level_reached;
+
+ pr_debug("p|v=0x%x\n", voltage_level | pre_emphasis_level);
+ return ctrl->aux->write(ctrl->aux, 0x103, 4, AUX_NATIVE, buf);
+}
+
+static void dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
+{
+ struct dp_link *link = ctrl->link;
+
+ pr_debug("v=%d p=%d\n", link->v_level, link->p_level);
+
+ ctrl->catalog->update_vx_px(ctrl->catalog,
+ link->v_level, link->p_level);
+
+ dp_ctrl_update_sink_vx_px(ctrl, link->v_level, link->p_level);
+}
+
+static void dp_ctrl_cap_lane_rate_set(struct dp_ctrl_private *ctrl)
+{
+ u8 buf[4];
+ struct dp_panel_dpcd *cap;
+
+ cap = &ctrl->panel->dpcd;
+
+ pr_debug("bw=%x lane=%d\n", ctrl->link->link_rate,
+ ctrl->link->lane_count);
+
+ buf[0] = ctrl->link->link_rate;
+ buf[1] = ctrl->link->lane_count;
+
+ if (cap->enhanced_frame)
+ buf[1] |= 0x80;
+
+ ctrl->aux->write(ctrl->aux, 0x100, 2, AUX_NATIVE, buf);
+}
+
+static void dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
+ u8 pattern)
+{
+ u8 buf[4];
+
+ pr_debug("pattern=%x\n", pattern);
+
+ buf[0] = pattern;
+ ctrl->aux->write(ctrl->aux, 0x102, 1, AUX_NATIVE, buf);
+}
+
+static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
+{
+ int tries, old_v_level;
+ int ret = 0;
+ int usleep_time;
+ int const maximum_retries = 5;
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+ ctrl->catalog->set_pattern(ctrl->catalog, 0x01);
+ dp_ctrl_cap_lane_rate_set(ctrl);
+ dp_ctrl_train_pattern_set(ctrl, 0x21); /* train_1 */
+ dp_ctrl_update_vx_px(ctrl);
+
+ tries = 0;
+ old_v_level = ctrl->link->v_level;
+ while (1) {
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+
+ if (ctrl->link->clock_recovery(ctrl->link)) {
+ ret = 0;
+ break;
+ }
+
+ if (ctrl->link->v_level == DP_LINK_VOLTAGE_MAX) {
+ ret = -1;
+ break; /* quit */
+ }
+
+ if (old_v_level == ctrl->link->v_level) {
+ tries++;
+ if (tries >= maximum_retries) {
+ ret = -1;
+ break; /* quit */
+ }
+ } else {
+ tries = 0;
+ old_v_level = ctrl->link->v_level;
+ }
+
+ ctrl->link->adjust_levels(ctrl->link);
+
+ dp_ctrl_update_vx_px(ctrl);
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (!ctrl)
+ return -EINVAL;
+
+ switch (ctrl->link->link_rate) {
+ case DP_LINK_RATE_810:
+ ctrl->link->link_rate = DP_LINK_RATE_540;
+ break;
+ case DP_LINK_RATE_540:
+ ctrl->link->link_rate = DP_LINK_RATE_270;
+ break;
+ case DP_LINK_RATE_270:
+ ctrl->link->link_rate = DP_LINK_RATE_162;
+ break;
+ case DP_LINK_RATE_162:
+ default:
+ ret = -EINVAL;
+ break;
+ };
+
+ pr_debug("new rate=%d\n", ctrl->link->link_rate);
+
+ return ret;
+}
+
+static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
+{
+ int usleep_time;
+
+ dp_ctrl_train_pattern_set(ctrl, 0);
+
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+}
+
+static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
+{
+ int tries = 0;
+ int ret = 0;
+ int usleep_time;
+ char pattern;
+ int const maximum_retries = 5;
+
+ if (ctrl->panel->dpcd.flags & DPCD_TPS3)
+ pattern = 0x03;
+ else
+ pattern = 0x02;
+
+ dp_ctrl_update_vx_px(ctrl);
+ ctrl->catalog->set_pattern(ctrl->catalog, pattern);
+ dp_ctrl_train_pattern_set(ctrl, pattern | 0x20);
+
+ do {
+ usleep_time = ctrl->panel->dpcd.training_read_interval;
+ usleep_range(usleep_time, usleep_time * 2);
+
+ if (ctrl->link->channel_equalization(ctrl->link)) {
+ ret = 0;
+ break;
+ }
+
+ if (tries > maximum_retries) {
+ ret = -1;
+ break;
+ }
+ tries++;
+
+ ctrl->link->adjust_levels(ctrl->link);
+
+ dp_ctrl_update_vx_px(ctrl);
+ } while (1);
+
+ return ret;
+}
+
+static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ ret = ctrl->aux->ready(ctrl->aux);
+ if (!ret) {
+ pr_err("aux chan NOT ready\n");
+ return ret;
+ }
+
+ ctrl->link->p_level = 0;
+ ctrl->link->v_level = 0;
+
+ dp_ctrl_config_ctrl(ctrl);
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ ret = dp_ctrl_link_train_1(ctrl);
+ if (ret < 0) {
+ if (!dp_ctrl_link_rate_down_shift(ctrl)) {
+ pr_debug("retry with lower rate\n");
+
+ dp_ctrl_clear_training_pattern(ctrl);
+ return -EAGAIN;
+ }
+
+ pr_err("Training 1 failed\n");
+ ret = -EINVAL;
+ goto clear;
+ }
+
+ pr_debug("Training 1 completed successfully\n");
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+ ret = dp_ctrl_link_training_2(ctrl);
+ if (ret < 0) {
+ if (!dp_ctrl_link_rate_down_shift(ctrl)) {
+ pr_debug("retry with lower rate\n");
+
+ dp_ctrl_clear_training_pattern(ctrl);
+ return -EAGAIN;
+ }
+
+ pr_err("Training 2 failed\n");
+ ret = -EINVAL;
+ goto clear;
+ }
+
+ pr_debug("Training 2 completed successfully\n");
+
+ dp_ctrl_state_ctrl(ctrl, 0);
+ /* Make sure to clear the current pattern before starting a new one */
+ wmb();
+
+clear:
+ dp_ctrl_clear_training_pattern(ctrl);
+ return ret;
+}
+
+static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
+{
+ bool mainlink_ready = false;
+ int ret = 0;
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+ dp_ctrl_set_sink_power_state(ctrl, SINK_POWER_ON);
+
+ if (ctrl->link->phy_pattern_requested(ctrl->link))
+ goto end;
+
+ if (!train)
+ goto send_video;
+
+ /*
+ * As part of previous calls, DP controller state might have
+ * transitioned to PUSH_IDLE. In order to start transmitting a link
+ * training pattern, we have to first to a DP software reset.
+ */
+ ctrl->catalog->reset(ctrl->catalog);
+
+ ret = dp_ctrl_link_train(ctrl);
+ if (ret)
+ goto end;
+
+send_video:
+ /*
+ * Set up transfer unit values and set controller state to send
+ * video.
+ */
+ dp_ctrl_setup_tr_unit(ctrl);
+ ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
+
+ dp_ctrl_wait4video_ready(ctrl);
+ mainlink_ready = ctrl->catalog->mainlink_ready(ctrl->catalog);
+ pr_debug("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
+end:
+ return ret;
+}
+
+static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
+ char *name, u32 rate)
+{
+ u32 num = ctrl->parser->mp[DP_CTRL_PM].num_clk;
+ struct dss_clk *cfg = ctrl->parser->mp[DP_CTRL_PM].clk_config;
+
+ while (num && strcmp(cfg->clk_name, name)) {
+ num--;
+ cfg++;
+ }
+
+ if (num)
+ cfg->rate = rate;
+ else
+ pr_err("%s clock could not be set with rate %d\n", name, rate);
+}
+
+static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ ctrl->power->set_pixel_clk_parent(ctrl->power);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
+ (ctrl->link->link_rate * DP_LINK_RATE_MULTIPLIER) /
+ DP_KHZ_TO_HZ);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_crypto_clk", DP_CRYPTO_CLK_RATE_KHZ);
+
+ dp_ctrl_set_clock_rate(ctrl, "ctrl_pixel_clk", ctrl->pixel_rate);
+
+ ret = ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, true);
+ if (ret) {
+ pr_err("Unabled to start link clocks\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dp_ctrl_disable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+ return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
+}
+
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip)
+{
+ struct dp_ctrl_private *ctrl;
+ struct dp_catalog_ctrl *catalog;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->initialized) {
+ pr_debug("host init done already\n");
+ return 0;
+ }
+
+ ctrl->orientation = flip;
+ catalog = ctrl->catalog;
+
+ catalog->reset(ctrl->catalog);
+ catalog->phy_reset(ctrl->catalog);
+ catalog->enable_irq(ctrl->catalog, true);
+
+ ctrl->initialized = true;
+
+ return 0;
+}
+
+/**
+ * dp_ctrl_host_deinit() - Uninitialize DP controller
+ * @ctrl: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
+{
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ pr_err("Invalid input data\n");
+ return;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (!ctrl->initialized) {
+ pr_debug("host deinit done already\n");
+ return;
+ }
+
+ ctrl->catalog->enable_irq(ctrl->catalog, false);
+ ctrl->catalog->reset(ctrl->catalog);
+
+ /* Make sure DP is disabled before clk disable */
+ wmb();
+
+ dp_ctrl_disable_mainlink_clocks(ctrl);
+
+ ctrl->initialized = false;
+ pr_debug("Host deinitialized successfully\n");
+}
+
+static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)
+{
+ int ret = 0;
+
+ do {
+ if (ret == -EAGAIN)
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+ ctrl->orientation, ctrl->link->lane_count);
+
+ if (lt_needed) {
+ /*
+ * Diasable and re-enable the mainlink clock since the
+ * link clock might have been adjusted as part of the
+ * link maintenance.
+ */
+ if (!ctrl->link->phy_pattern_requested(
+ ctrl->link))
+ dp_ctrl_disable_mainlink_clocks(ctrl);
+
+ ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+ if (ret)
+ continue;
+ }
+
+ dp_ctrl_configure_source_params(ctrl);
+
+ reinit_completion(&ctrl->idle_comp);
+
+ ctrl->power_on = true;
+
+ if (ctrl->psm_enabled) {
+ ret = ctrl->link->send_psm_request(ctrl->link, false);
+ if (ret) {
+ pr_err("failed to exit low power mode, rc=%d\n",
+ ret);
+ continue;
+ }
+ }
+
+ ret = dp_ctrl_setup_main_link(ctrl, lt_needed);
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ if (ctrl->cont_splash)
+ goto link_training;
+
+ ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
+ ctrl->catalog->hpd_config(ctrl->catalog, true);
+
+ ctrl->link->link_rate = ctrl->panel->get_link_rate(ctrl->panel);
+ ctrl->link->lane_count = ctrl->panel->dpcd.max_lane_count;
+ ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+
+ pr_debug("link_rate=%d, lane_count=%d, pixel_rate=%d\n",
+ ctrl->link->link_rate, ctrl->link->lane_count,
+ ctrl->pixel_rate);
+
+ ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+ ctrl->orientation, ctrl->link->lane_count);
+
+ ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+ if (ret)
+ goto exit;
+
+ reinit_completion(&ctrl->idle_comp);
+
+ dp_ctrl_configure_source_params(ctrl);
+
+ if (ctrl->psm_enabled)
+ ret = ctrl->link->send_psm_request(ctrl->link, false);
+link_training:
+ ctrl->power_on = true;
+
+ while (-EAGAIN == dp_ctrl_setup_main_link(ctrl, true))
+ pr_debug("MAIN LINK TRAINING RETRY\n");
+
+ ctrl->cont_splash = 0;
+
+ ctrl->power_on = true;
+ pr_debug("End-\n");
+
+exit:
+ return ret;
+}
+
+static int dp_ctrl_off_irq(struct dp_ctrl_private *ctrl)
+{
+ if (!ctrl->power_on) {
+ pr_debug("ctrl already powered off\n");
+ return 0;
+ }
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ /* Make sure DP mainlink and audio engines are disabled */
+ wmb();
+
+ complete_all(&ctrl->irq_comp);
+ pr_debug("end\n");
+
+ return 0;
+}
+
+static int dp_ctrl_off_hpd(struct dp_ctrl_private *ctrl)
+{
+ if (!ctrl->power_on) {
+ pr_debug("panel already powered off\n");
+ return 0;
+ }
+
+ ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+ ctrl->power_on = false;
+ ctrl->sink_info_read = false;
+
+ pr_debug("DP off done\n");
+
+ return 0;
+}
+
+static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->hpd_irq_on)
+ rc = dp_ctrl_on_irq(ctrl, false);
+ else
+ rc = dp_ctrl_on_hpd(ctrl);
+end:
+ return rc;
+}
+
+static int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ if (ctrl->hpd_irq_on)
+ rc = dp_ctrl_off_irq(ctrl);
+ else
+ rc = dp_ctrl_off_hpd(ctrl);
+end:
+ return rc;
+}
+
+static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl, u32 irq)
+{
+ u32 isr;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl)
+ return;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ isr = ctrl->catalog->get_interrupt(ctrl->catalog);
+
+ if (isr & DP_CTRL_INTR_READY_FOR_VIDEO)
+ dp_ctrl_video_ready(ctrl);
+
+ if (isr & DP_CTRL_INTR_IDLE_PATTERN_SENT)
+ dp_ctrl_idle_patterns_sent(ctrl);
+}
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
+{
+ int rc = 0;
+ struct dp_ctrl_private *ctrl;
+ struct dp_ctrl *dp_ctrl;
+
+ if (!in->dev || !in->panel || !in->aux ||
+ !in->link || !in->catalog) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ ctrl = devm_kzalloc(in->dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ init_completion(&ctrl->idle_comp);
+ init_completion(&ctrl->video_comp);
+ init_completion(&ctrl->irq_comp);
+
+ /* in parameters */
+ ctrl->parser = in->parser;
+ ctrl->panel = in->panel;
+ ctrl->power = in->power;
+ ctrl->aux = in->aux;
+ ctrl->link = in->link;
+ ctrl->catalog = in->catalog;
+
+ dp_ctrl = &ctrl->dp_ctrl;
+
+ /* out parameters */
+ dp_ctrl->init = dp_ctrl_host_init;
+ dp_ctrl->deinit = dp_ctrl_host_deinit;
+ dp_ctrl->on = dp_ctrl_on;
+ dp_ctrl->off = dp_ctrl_off;
+ dp_ctrl->push_idle = dp_ctrl_push_idle;
+ dp_ctrl->isr = dp_ctrl_isr;
+
+ return dp_ctrl;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl)
+{
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl)
+ return;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ devm_kfree(ctrl->dev, ctrl);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
new file mode 100644
index 0000000..c9cf7f8
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_CTRL_H_
+#define _DP_CTRL_H_
+
+#include "dp_aux.h"
+#include "dp_panel.h"
+#include "dp_link.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+
+struct dp_ctrl {
+ int (*init)(struct dp_ctrl *dp_ctrl, bool flip);
+ void (*deinit)(struct dp_ctrl *dp_ctrl);
+ int (*on)(struct dp_ctrl *dp_ctrl);
+ int (*off)(struct dp_ctrl *dp_ctrl);
+ void (*push_idle)(struct dp_ctrl *dp_ctrl);
+ void (*isr)(struct dp_ctrl *dp_ctrl, u32 isr);
+};
+
+struct dp_ctrl_in {
+ struct device *dev;
+ struct dp_panel *panel;
+ struct dp_aux *aux;
+ struct dp_link *link;
+ struct dp_parser *parser;
+ struct dp_power *power;
+ struct dp_catalog_ctrl *catalog;
+};
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
+
+#endif /* _DP_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
new file mode 100644
index 0000000..e3c30a5
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp]: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/component.h>
+#include <linux/of_irq.h>
+
+#include "dp_usbpd.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_panel.h"
+#include "dp_ctrl.h"
+#include "dp_display.h"
+
+static struct dp_display *g_dp_display;
+
+struct dp_display_private {
+ char *name;
+ int irq;
+
+ struct platform_device *pdev;
+ struct dentry *root;
+ struct mutex lock;
+
+ struct dp_usbpd *usbpd;
+ struct dp_parser *parser;
+ struct dp_power *power;
+ struct dp_catalog *catalog;
+ struct dp_aux *aux;
+ struct dp_link *link;
+ struct dp_panel *panel;
+ struct dp_ctrl *ctrl;
+
+ struct dp_usbpd_cb usbpd_cb;
+ struct dp_display_mode mode;
+ struct dp_display dp_display;
+};
+
+static const struct of_device_id dp_dt_match[] = {
+ {.compatible = "qcom,dp-display"},
+ {}
+};
+
+static ssize_t debugfs_dp_info_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ struct dp_display_private *dp = file->private_data;
+ char *buf;
+ u32 len = 0;
+
+ if (!dp)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ buf = kzalloc(SZ_4K, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ len += snprintf(buf + len, (SZ_4K - len), "name = %s\n", dp->name);
+ len += snprintf(buf + len, (SZ_4K - len),
+ "\tResolution = %dx%d\n",
+ dp->panel->pinfo.h_active,
+ dp->panel->pinfo.v_active);
+
+ if (copy_to_user(buff, buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ *ppos += len;
+
+ kfree(buf);
+ return len;
+}
+
+static const struct file_operations dp_debug_fops = {
+ .open = simple_open,
+ .read = debugfs_dp_info_read,
+};
+
+static int dp_display_debugfs_init(struct dp_display_private *dp)
+{
+ int rc = 0;
+ struct dentry *dir, *file;
+
+ dir = debugfs_create_dir(dp->name, NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ rc = PTR_ERR(dir);
+ pr_err("[%s] debugfs create dir failed, rc = %d\n",
+ dp->name, rc);
+ goto error;
+ }
+
+ file = debugfs_create_file("dp_debug", 0444, dir, dp, &dp_debug_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs create file failed, rc=%d\n",
+ dp->name, rc);
+ goto error_remove_dir;
+ }
+
+ dp->root = dir;
+ return rc;
+error_remove_dir:
+ debugfs_remove(dir);
+error:
+ return rc;
+}
+
+static int dp_display_debugfs_deinit(struct dp_display_private *dp)
+{
+ debugfs_remove(dp->root);
+ return 0;
+}
+
+static int dp_display_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+ struct drm_device *drm;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (!dev || !pdev || !master) {
+ pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
+ dev, pdev, master);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ drm = dev_get_drvdata(master);
+ dp = platform_get_drvdata(pdev);
+ if (!drm || !dp) {
+ pr_err("invalid param(s), drm %pK, dp %pK\n",
+ drm, dp);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp->dp_display.drm_dev = drm;
+
+ mutex_lock(&dp->lock);
+
+ rc = dp_display_debugfs_init(dp);
+ if (rc) {
+ pr_err("[%s]Debugfs init failed, rc=%d\n", dp->name, rc);
+ goto end;
+ }
+
+ rc = dp->parser->parse(dp->parser);
+ if (rc) {
+ pr_err("device tree parsing failed\n");
+ goto end;
+ }
+end:
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static void dp_display_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dp_display_private *dp;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (!dev || !pdev) {
+ pr_err("invalid param(s)\n");
+ return;
+ }
+
+ dp = platform_get_drvdata(pdev);
+ if (!dp) {
+ pr_err("Invalid params\n");
+ return;
+ }
+
+ mutex_lock(&dp->lock);
+
+ (void)dp_display_debugfs_deinit(dp);
+
+ mutex_unlock(&dp->lock);
+}
+
+static const struct component_ops dp_display_comp_ops = {
+ .bind = dp_display_bind,
+ .unbind = dp_display_unbind,
+};
+
+static int dp_display_process_hpd_high(struct dp_display_private *dp)
+{
+ int rc;
+
+ rc = dp->panel->read_dpcd(dp->panel);
+ if (rc)
+ goto end;
+
+ rc = dp->panel->read_edid(dp->panel);
+ if (rc)
+ goto end;
+
+ return 0;
+end:
+ return rc;
+}
+
+static int dp_display_process_hpd_low(struct dp_display_private *dp)
+{
+ return 0;
+}
+
+static irqreturn_t dp_display_irq(int irq, void *dev_id)
+{
+ struct dp_display_private *dp = dev_id;
+
+ if (!dp) {
+ pr_err("invalid data\n");
+ return IRQ_NONE;
+ }
+
+ dp->aux->isr(dp->aux);
+
+ return IRQ_HANDLED;
+}
+
+static int dp_display_usbpd_configure_cb(struct device *dev)
+{
+ int rc = 0;
+ bool flip = false;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ if (!dp->irq) {
+ dp->irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (dp->irq < 0) {
+ rc = dp->irq;
+ pr_err("failed to get irq: %d\n", rc);
+ goto end;
+ }
+
+ rc = devm_request_irq(dev, dp->irq,
+ dp_display_irq, IRQF_TRIGGER_HIGH,
+ "dp_display_isr", dp);
+ if (rc < 0) {
+ pr_err("failed to request IRQ%u: %d\n",
+ dp->irq, rc);
+ goto end;
+ }
+ }
+
+ mutex_lock(&dp->lock);
+
+ if (dp->usbpd->orientation == ORIENTATION_CC2)
+ flip = true;
+
+ dp->power->init(dp->power, flip);
+ dp->ctrl->init(dp->ctrl, flip);
+ dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+ if (dp->usbpd->hpd_high)
+ dp_display_process_hpd_high(dp);
+
+ mutex_unlock(&dp->lock);
+end:
+ return rc;
+}
+
+static int dp_display_usbpd_disconnect_cb(struct device *dev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ mutex_lock(&dp->lock);
+ mutex_unlock(&dp->lock);
+
+end:
+ return rc;
+}
+
+static int dp_display_usbpd_attention_cb(struct device *dev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dev) {
+ pr_err("invalid dev\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ dp = dev_get_drvdata(dev);
+ if (!dp) {
+ pr_err("no driver data found\n");
+ rc = -ENODEV;
+ goto end;
+ }
+
+ mutex_lock(&dp->lock);
+
+ if (dp->usbpd->hpd_irq) {
+ if (!dp->link->process_request(dp->link))
+ goto end;
+ }
+
+ if (dp->usbpd->hpd_high)
+ dp_display_process_hpd_high(dp);
+ else
+ dp_display_process_hpd_low(dp);
+
+ mutex_unlock(&dp->lock);
+end:
+ return rc;
+}
+
+static int dp_init_sub_modules(struct dp_display_private *dp)
+{
+ int rc = 0;
+ struct device *dev = &dp->pdev->dev;
+ struct dp_usbpd_cb *cb = &dp->usbpd_cb;
+ struct dp_ctrl_in ctrl_in = {
+ .dev = dev,
+ };
+
+ cb->configure = dp_display_usbpd_configure_cb;
+ cb->disconnect = dp_display_usbpd_disconnect_cb;
+ cb->attention = dp_display_usbpd_attention_cb;
+
+ dp->usbpd = dp_usbpd_get(dev, cb);
+ if (IS_ERR(dp->usbpd)) {
+ rc = PTR_ERR(dp->usbpd);
+ pr_err("failed to initialize usbpd, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->parser = dp_parser_get(dp->pdev);
+ if (IS_ERR(dp->parser)) {
+ rc = PTR_ERR(dp->parser);
+ pr_err("failed to initialize parser, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->catalog = dp_catalog_get(dev, &dp->parser->io);
+ if (IS_ERR(dp->catalog)) {
+ rc = PTR_ERR(dp->catalog);
+ pr_err("failed to initialize catalog, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->power = dp_power_get(dp->parser);
+ if (IS_ERR(dp->power)) {
+ rc = PTR_ERR(dp->power);
+ pr_err("failed to initialize power, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->aux = dp_aux_get(dev, &dp->catalog->aux);
+ if (IS_ERR(dp->aux)) {
+ rc = PTR_ERR(dp->aux);
+ pr_err("failed to initialize aux, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->panel = dp_panel_get(dev, dp->aux, &dp->catalog->panel);
+ if (IS_ERR(dp->panel)) {
+ rc = PTR_ERR(dp->panel);
+ pr_err("failed to initialize panel, rc = %d\n", rc);
+ goto err;
+ }
+
+ dp->link = dp_link_get(dev, dp->aux);
+ if (IS_ERR(dp->link)) {
+ rc = PTR_ERR(dp->link);
+ pr_err("failed to initialize link, rc = %d\n", rc);
+ goto err;
+ }
+
+ ctrl_in.link = dp->link;
+ ctrl_in.panel = dp->panel;
+ ctrl_in.aux = dp->aux;
+ ctrl_in.power = dp->power;
+ ctrl_in.catalog = &dp->catalog->ctrl;
+ ctrl_in.parser = dp->parser;
+
+ dp->ctrl = dp_ctrl_get(&ctrl_in);
+ if (IS_ERR(dp->ctrl)) {
+ rc = PTR_ERR(dp->ctrl);
+ pr_err("failed to initialize ctrl, rc = %d\n", rc);
+ goto err;
+ }
+err:
+ return rc;
+}
+
+static int dp_display_set_mode(struct dp_display *dp_display,
+ struct dp_display_mode *mode)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ dp->panel->pinfo = mode->timing;
+ dp->panel->init_info(dp->panel);
+error:
+ return rc;
+}
+
+static int dp_display_prepare(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_enable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+ dp->ctrl->on(dp->ctrl);
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_post_enable(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_pre_disable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+
+ dp->ctrl->off(dp->ctrl);
+
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_disable(struct dp_display *dp_display)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!dp_display) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+ mutex_lock(&dp->lock);
+
+ dp->aux->deinit(dp->aux);
+ dp->ctrl->deinit(dp->ctrl);
+ dp->power->deinit(dp->power);
+
+ mutex_unlock(&dp->lock);
+error:
+ return rc;
+}
+
+static int dp_display_unprepare(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_validate_mode(struct dp_display *dp,
+ struct dp_display_mode *mode)
+{
+ return 0;
+}
+
+static int dp_display_get_modes(struct dp_display *dp,
+ struct dp_display_mode *modes, u32 *count)
+{
+ *count = 1;
+
+ if (modes) {
+ modes->timing.h_active = 1920;
+ modes->timing.v_active = 1080;
+ modes->timing.h_back_porch = 148;
+ modes->timing.h_front_porch = 88;
+ modes->timing.h_sync_width = 44;
+ modes->timing.h_active_low = 0;
+ modes->timing.v_back_porch = 36;
+ modes->timing.v_front_porch = 4;
+ modes->timing.v_sync_width = 5;
+ modes->timing.v_active_low = 0;
+ modes->timing.h_skew = 0;
+ modes->timing.refresh_rate = 60;
+ modes->timing.pixel_clk_khz = 148500;
+ }
+
+ return 0;
+}
+
+static int dp_display_detect(struct dp_display *dp)
+{
+ return 0;
+}
+
+static int dp_display_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct dp_display_private *dp;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("pdev not found\n");
+ return -ENODEV;
+ }
+
+ dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ mutex_init(&dp->lock);
+ dp->pdev = pdev;
+ dp->name = "drm_dp";
+
+ rc = dp_init_sub_modules(dp);
+ if (rc) {
+ devm_kfree(&pdev->dev, dp);
+ return -EPROBE_DEFER;
+ }
+
+ platform_set_drvdata(pdev, dp);
+
+ g_dp_display = &dp->dp_display;
+
+ g_dp_display->enable = dp_display_enable;
+ g_dp_display->post_enable = dp_display_post_enable;
+ g_dp_display->pre_disable = dp_display_pre_disable;
+ g_dp_display->disable = dp_display_disable;
+ g_dp_display->set_mode = dp_display_set_mode;
+ g_dp_display->validate_mode = dp_display_validate_mode;
+ g_dp_display->get_modes = dp_display_get_modes;
+ g_dp_display->detect = dp_display_detect;
+ g_dp_display->prepare = dp_display_prepare;
+ g_dp_display->unprepare = dp_display_unprepare;
+
+ rc = component_add(&pdev->dev, &dp_display_comp_ops);
+ if (rc)
+ pr_err("component add failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+struct dp_display *dp_display_get(void)
+{
+ return g_dp_display;
+}
+
+static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
+{
+ dp_ctrl_put(dp->ctrl);
+ dp_link_put(dp->link);
+ dp_panel_put(dp->panel);
+ dp_aux_put(dp->aux);
+ dp_power_put(dp->power);
+ dp_catalog_put(dp->catalog);
+ dp_parser_put(dp->parser);
+ dp_usbpd_put(dp->usbpd);
+}
+
+static int dp_display_remove(struct platform_device *pdev)
+{
+ struct dp_display_private *dp;
+
+ if (!pdev)
+ return -EINVAL;
+
+ dp = platform_get_drvdata(pdev);
+
+ dp_display_deinit_sub_modules(dp);
+
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, dp);
+
+ return 0;
+}
+
+static struct platform_driver dp_display_driver = {
+ .probe = dp_display_probe,
+ .remove = dp_display_remove,
+ .driver = {
+ .name = "msm-dp-display",
+ .of_match_table = dp_dt_match,
+ },
+};
+
+static int __init dp_display_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&dp_display_driver);
+ if (ret) {
+ pr_err("driver register failed");
+ return ret;
+ }
+
+ return ret;
+}
+module_init(dp_display_init);
+
+static void __exit dp_display_cleanup(void)
+{
+ platform_driver_unregister(&dp_display_driver);
+}
+module_exit(dp_display_cleanup);
+
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
new file mode 100644
index 0000000..2bc591c
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_DISPLAY_H_
+#define _DP_DISPLAY_H_
+
+#include <drm/drmP.h>
+
+#include "dp_panel.h"
+
+struct dp_display_mode {
+ struct dp_panel_info timing;
+ u32 capabilities;
+};
+
+struct dp_display {
+ struct drm_device *drm_dev;
+ struct dp_bridge *bridge;
+
+ int (*enable)(struct dp_display *dp_display);
+ int (*post_enable)(struct dp_display *dp_display);
+
+ int (*pre_disable)(struct dp_display *dp_display);
+ int (*disable)(struct dp_display *dp_display);
+
+ int (*set_mode)(struct dp_display *dp_display,
+ struct dp_display_mode *mode);
+ int (*validate_mode)(struct dp_display *dp_display,
+ struct dp_display_mode *mode);
+ int (*get_modes)(struct dp_display *dp_display,
+ struct dp_display_mode *modes, u32 *count);
+
+ int (*detect)(struct dp_display *dp_display);
+
+ int (*prepare)(struct dp_display *dp_display);
+ int (*unprepare)(struct dp_display *dp_display);
+};
+
+struct dp_display *dp_display_get(void);
+#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
new file mode 100644
index 0000000..e9955a9
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -0,0 +1,1809 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include "dp_link.h"
+#include "dp_panel.h"
+
+#define DP_LINK_ENUM_STR(x) #x
+
+enum dp_lane_count {
+ DP_LANE_COUNT_1 = 1,
+ DP_LANE_COUNT_2 = 2,
+ DP_LANE_COUNT_4 = 4,
+};
+
+enum phy_test_pattern {
+ PHY_TEST_PATTERN_NONE,
+ PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING,
+ PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT,
+ PHY_TEST_PATTERN_PRBS7,
+ PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN,
+ PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN,
+};
+
+enum dynamic_range {
+ DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
+ DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
+ DP_DYNAMIC_RANGE_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum test_video_pattern {
+ DP_TEST_VIDEO_PATTERN_NONE = 0x00,
+ DP_TEST_VIDEO_PATTERN_COLOR_RAMPS = 0x01,
+ DP_TEST_VIDEO_PATTERN_BW_VERT_LINES = 0x02,
+ DP_TEST_VIDEO_PATTERN_COLOR_SQUARE = 0x03,
+};
+
+enum test_bit_depth {
+ DP_TEST_BIT_DEPTH_6 = 0x00,
+ DP_TEST_BIT_DEPTH_8 = 0x01,
+ DP_TEST_BIT_DEPTH_10 = 0x02,
+ DP_TEST_BIT_DEPTH_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum dp_link_response {
+ TEST_ACK = 0x1,
+ TEST_NACK = 0x2,
+ TEST_EDID_CHECKSUM_WRITE = 0x4,
+};
+
+enum audio_sample_rate {
+ AUDIO_SAMPLE_RATE_32_KHZ = 0x00,
+ AUDIO_SAMPLE_RATE_44_1_KHZ = 0x01,
+ AUDIO_SAMPLE_RATE_48_KHZ = 0x02,
+ AUDIO_SAMPLE_RATE_88_2_KHZ = 0x03,
+ AUDIO_SAMPLE_RATE_96_KHZ = 0x04,
+ AUDIO_SAMPLE_RATE_176_4_KHZ = 0x05,
+ AUDIO_SAMPLE_RATE_192_KHZ = 0x06,
+};
+
+enum audio_pattern_type {
+ AUDIO_TEST_PATTERN_OPERATOR_DEFINED = 0x00,
+ AUDIO_TEST_PATTERN_SAWTOOTH = 0x01,
+};
+
+struct dp_link_request {
+ u32 test_requested;
+ u32 test_link_rate;
+ u32 test_lane_count;
+ u32 phy_test_pattern_sel;
+ u32 test_video_pattern;
+ u32 test_bit_depth;
+ u32 test_dyn_range;
+ u32 test_h_total;
+ u32 test_v_total;
+ u32 test_h_start;
+ u32 test_v_start;
+ u32 test_hsync_pol;
+ u32 test_hsync_width;
+ u32 test_vsync_pol;
+ u32 test_vsync_width;
+ u32 test_h_width;
+ u32 test_v_height;
+ u32 test_rr_d;
+ u32 test_rr_n;
+ u32 test_audio_sampling_rate;
+ u32 test_audio_channel_count;
+ u32 test_audio_pattern_type;
+ u32 test_audio_period_ch_1;
+ u32 test_audio_period_ch_2;
+ u32 test_audio_period_ch_3;
+ u32 test_audio_period_ch_4;
+ u32 test_audio_period_ch_5;
+ u32 test_audio_period_ch_6;
+ u32 test_audio_period_ch_7;
+ u32 test_audio_period_ch_8;
+ u32 response;
+};
+
+struct dp_link_sink_count {
+ u32 count;
+ bool cp_ready;
+};
+
+struct dp_link_status {
+ u8 lane_01_status;
+ u8 lane_23_status;
+ u8 interlane_align_done;
+ u8 downstream_port_status_changed;
+ u8 link_status_updated;
+ u8 port_0_in_sync;
+ u8 port_1_in_sync;
+ u8 req_voltage_swing[4];
+ u8 req_pre_emphasis[4];
+};
+
+struct dp_link_private {
+ struct device *dev;
+ struct dp_aux *aux;
+ struct dp_link dp_link;
+
+ struct dp_link_request request;
+ struct dp_link_sink_count sink_count;
+ struct dp_link_status link_status;
+};
+
+/**
+ * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
+ * @tbd: test bit depth
+ *
+ * Returns the bits per pixel (bpp) to be used corresponding to the
+ * git bit depth value. This function assumes that bit depth has
+ * already been validated.
+ */
+static inline u32 dp_link_bit_depth_to_bpp(enum test_bit_depth tbd)
+{
+ u32 bpp;
+
+ /*
+ * Few simplistic rules and assumptions made here:
+ * 1. Bit depth is per color component
+ * 2. If bit depth is unknown return 0
+ * 3. Assume 3 color components
+ */
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ bpp = 18;
+ break;
+ case DP_TEST_BIT_DEPTH_8:
+ bpp = 24;
+ break;
+ case DP_TEST_BIT_DEPTH_10:
+ bpp = 30;
+ break;
+ case DP_TEST_BIT_DEPTH_UNKNOWN:
+ default:
+ bpp = 0;
+ }
+
+ return bpp;
+}
+
+static char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_NONE);
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING);
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ return DP_LINK_ENUM_STR(
+ PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
+ case PHY_TEST_PATTERN_PRBS7:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_PRBS7);
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN);
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return DP_LINK_ENUM_STR(PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN);
+ default:
+ return "unknown";
+ }
+}
+
+static char *dp_link_get_audio_test_pattern(u32 pattern)
+{
+ switch (pattern) {
+ case AUDIO_TEST_PATTERN_OPERATOR_DEFINED:
+ return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_OPERATOR_DEFINED);
+ case AUDIO_TEST_PATTERN_SAWTOOTH:
+ return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_SAWTOOTH);
+ default:
+ return "unknown";
+ }
+}
+
+static char *dp_link_get_audio_sample_rate(u32 rate)
+{
+ switch (rate) {
+ case AUDIO_SAMPLE_RATE_32_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_32_KHZ);
+ case AUDIO_SAMPLE_RATE_44_1_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_44_1_KHZ);
+ case AUDIO_SAMPLE_RATE_48_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_48_KHZ);
+ case AUDIO_SAMPLE_RATE_88_2_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_88_2_KHZ);
+ case AUDIO_SAMPLE_RATE_96_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_96_KHZ);
+ case AUDIO_SAMPLE_RATE_176_4_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_176_4_KHZ);
+ case AUDIO_SAMPLE_RATE_192_KHZ:
+ return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_192_KHZ);
+ default:
+ return "unknown";
+ }
+}
+
+static int dp_link_get_period(struct dp_link_private *link, int const addr)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ u32 const param_len = 0x1;
+ u32 const max_audio_period = 0xA;
+
+ /* TEST_AUDIO_PERIOD_CH_XX */
+ rlen = link->aux->read(link->aux, addr, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read test_audio_period (0x%x)\n", addr);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ data = *bp;
+
+ /* Period - Bits 3:0 */
+ data = data & 0xF;
+ if ((int)data > max_audio_period) {
+ pr_err("invalid test_audio_period_ch_1 = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = data;
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
+{
+ int ret = 0;
+ int const test_audio_period_ch_1_addr = 0x273;
+ int const test_audio_period_ch_2_addr = 0x274;
+ int const test_audio_period_ch_3_addr = 0x275;
+ int const test_audio_period_ch_4_addr = 0x276;
+ int const test_audio_period_ch_5_addr = 0x277;
+ int const test_audio_period_ch_6_addr = 0x278;
+ int const test_audio_period_ch_7_addr = 0x279;
+ int const test_audio_period_ch_8_addr = 0x27A;
+ struct dp_link_request *req = &link->request;
+
+ /* TEST_AUDIO_PERIOD_CH_1 (Byte 0x273) */
+ ret = dp_link_get_period(link, test_audio_period_ch_1_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_1 = ret;
+ pr_debug("test_audio_period_ch_1 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_2 (Byte 0x274) */
+ ret = dp_link_get_period(link, test_audio_period_ch_2_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_2 = ret;
+ pr_debug("test_audio_period_ch_2 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
+ ret = dp_link_get_period(link, test_audio_period_ch_3_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_3 = ret;
+ pr_debug("test_audio_period_ch_3 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_4 (Byte 0x276) */
+ ret = dp_link_get_period(link, test_audio_period_ch_4_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_4 = ret;
+ pr_debug("test_audio_period_ch_4 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_5 (Byte 0x277) */
+ ret = dp_link_get_period(link, test_audio_period_ch_5_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_5 = ret;
+ pr_debug("test_audio_period_ch_5 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_6 (Byte 0x278) */
+ ret = dp_link_get_period(link, test_audio_period_ch_6_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_6 = ret;
+ pr_debug("test_audio_period_ch_6 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_7 (Byte 0x279) */
+ ret = dp_link_get_period(link, test_audio_period_ch_7_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_7 = ret;
+ pr_debug("test_audio_period_ch_7 = 0x%x\n", ret);
+
+ /* TEST_AUDIO_PERIOD_CH_8 (Byte 0x27A) */
+ ret = dp_link_get_period(link, test_audio_period_ch_8_addr);
+ if (ret == -EINVAL)
+ goto exit;
+
+ req->test_audio_period_ch_8 = ret;
+ pr_debug("test_audio_period_ch_8 = 0x%x\n", ret);
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_audio_pattern_type_addr = 0x272;
+ int const max_audio_pattern_type = 0x1;
+
+ /* Read the requested audio pattern type (Byte 0x272). */
+ rlen = link->aux->read(link->aux, test_audio_pattern_type_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link audio mode data\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Audio Pattern Type - Bits 7:0 */
+ if ((int)data > max_audio_pattern_type) {
+ pr_err("invalid audio pattern type = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_audio_pattern_type = data;
+ pr_debug("audio pattern type = %s\n",
+ dp_link_get_audio_test_pattern(data));
+exit:
+ return ret;
+}
+
+static int dp_link_parse_audio_mode(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_audio_mode_addr = 0x271;
+ int const max_audio_sampling_rate = 0x6;
+ int const max_audio_channel_count = 0x8;
+ int sampling_rate = 0x0;
+ int channel_count = 0x0;
+
+ /* Read the requested audio mode (Byte 0x271). */
+ rlen = link->aux->read(link->aux, test_audio_mode_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link audio mode data\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Sampling Rate - Bits 3:0 */
+ sampling_rate = data & 0xF;
+ if (sampling_rate > max_audio_sampling_rate) {
+ pr_err("sampling rate (0x%x) greater than max (0x%x)\n",
+ sampling_rate, max_audio_sampling_rate);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Channel Count - Bits 7:4 */
+ channel_count = ((data & 0xF0) >> 4) + 1;
+ if (channel_count > max_audio_channel_count) {
+ pr_err("channel_count (0x%x) greater than max (0x%x)\n",
+ channel_count, max_audio_channel_count);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_audio_sampling_rate = sampling_rate;
+ link->request.test_audio_channel_count = channel_count;
+ pr_debug("sampling_rate = %s, channel_count = 0x%x\n",
+ dp_link_get_audio_sample_rate(sampling_rate), channel_count);
+exit:
+ return ret;
+}
+
+/**
+ * dp_parse_audio_pattern_params() - parses audio pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the audio link pattern parameters.
+ */
+static int dp_link_parse_audio_pattern_params(struct dp_link_private *link)
+{
+ int ret = 0;
+
+ ret = dp_link_parse_audio_mode(link);
+ if (ret)
+ goto exit;
+
+ ret = dp_link_parse_audio_pattern_type(link);
+ if (ret)
+ goto exit;
+
+ ret = dp_link_parse_audio_channel_period(link);
+
+exit:
+ return ret;
+}
+
+/**
+ * dp_link_is_video_pattern_valid() - validates the video pattern
+ * @pattern: video pattern requested by the sink
+ *
+ * Returns true if the requested video pattern is supported.
+ */
+static bool dp_link_is_video_pattern_valid(u32 pattern)
+{
+ switch (pattern) {
+ case DP_TEST_VIDEO_PATTERN_NONE:
+ case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
+ case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
+ case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_video_pattern_to_string(u32 test_video_pattern)
+{
+ switch (test_video_pattern) {
+ case DP_TEST_VIDEO_PATTERN_NONE:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_NONE);
+ case DP_TEST_VIDEO_PATTERN_COLOR_RAMPS:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_RAMPS);
+ case DP_TEST_VIDEO_PATTERN_BW_VERT_LINES:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_BW_VERT_LINES);
+ case DP_TEST_VIDEO_PATTERN_COLOR_SQUARE:
+ return DP_LINK_ENUM_STR(DP_TEST_VIDEO_PATTERN_COLOR_SQUARE);
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_dynamic_range_valid() - validates the dynamic range
+ * @bit_depth: the dynamic range value to be checked
+ *
+ * Returns true if the dynamic range value is supported.
+ */
+static bool dp_link_is_dynamic_range_valid(u32 dr)
+{
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_dynamic_range_to_string(u32 dr)
+{
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_VESA);
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_CEA);
+ case DP_DYNAMIC_RANGE_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_bit_depth_valid() - validates the bit depth requested
+ * @bit_depth: bit depth requested by the sink
+ *
+ * Returns true if the requested bit depth is supported.
+ */
+static bool dp_link_is_bit_depth_valid(u32 tbd)
+{
+ /* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ case DP_TEST_BIT_DEPTH_8:
+ case DP_TEST_BIT_DEPTH_10:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static char *dp_link_bit_depth_to_string(u32 tbd)
+{
+ switch (tbd) {
+ case DP_TEST_BIT_DEPTH_6:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_6);
+ case DP_TEST_BIT_DEPTH_8:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_8);
+ case DP_TEST_BIT_DEPTH_10:
+ return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_10);
+ case DP_TEST_BIT_DEPTH_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+static int dp_link_parse_timing_params1(struct dp_link_private *link,
+ int const addr, int const len, u32 *val)
+{
+ u8 *bp;
+ int rlen;
+
+ if (len < 2)
+ return -EINVAL;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+
+ *val = bp[1] | (bp[0] << 8);
+
+ return 0;
+}
+
+static int dp_link_parse_timing_params2(struct dp_link_private *link,
+ int const addr, int const len, u32 *val1, u32 *val2)
+{
+ u8 *bp;
+ int rlen;
+
+ if (len < 2)
+ return -EINVAL;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+
+ *val1 = (bp[0] & BIT(7)) >> 7;
+ *val2 = bp[1] | ((bp[0] & 0x7F) << 8);
+
+ return 0;
+}
+
+static int dp_link_parse_timing_params3(struct dp_link_private *link,
+ int const addr, u32 *val)
+{
+ u8 *bp;
+ u32 len = 1;
+ int rlen;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < 1) {
+ pr_err("failed to read 0x%x\n", addr);
+ return -EINVAL;
+ }
+ *val = bp[0];
+
+ return 0;
+}
+
+/**
+ * dp_parse_video_pattern_params() - parses video pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the video link pattern and the link
+ * bit depth requested by the sink and, and if the values parsed are valid.
+ */
+static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
+{
+ int ret = 0;
+ int rlen;
+ u8 *bp;
+ u8 data;
+ u32 dyn_range;
+ int const param_len = 0x1;
+ int const test_video_pattern_addr = 0x221;
+ int const test_misc_addr = 0x232;
+
+ /* Read the requested video link pattern (Byte 0x221). */
+ rlen = link->aux->read(link->aux, test_video_pattern_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link video pattern\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ if (!dp_link_is_video_pattern_valid(data)) {
+ pr_err("invalid link video pattern = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_video_pattern = data;
+ pr_debug("link video pattern = 0x%x (%s)\n",
+ link->request.test_video_pattern,
+ dp_link_video_pattern_to_string(
+ link->request.test_video_pattern));
+
+ /* Read the requested color bit depth and dynamic range (Byte 0x232) */
+ rlen = link->aux->read(link->aux, test_misc_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link bit depth\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ /* Dynamic Range */
+ dyn_range = (data & BIT(3)) >> 3;
+ if (!dp_link_is_dynamic_range_valid(dyn_range)) {
+ pr_err("invalid link dynamic range = 0x%x", dyn_range);
+ ret = -EINVAL;
+ goto exit;
+ }
+ link->request.test_dyn_range = dyn_range;
+ pr_debug("link dynamic range = 0x%x (%s)\n",
+ link->request.test_dyn_range,
+ dp_link_dynamic_range_to_string(
+ link->request.test_dyn_range));
+
+ /* Color bit depth */
+ data &= (BIT(5) | BIT(6) | BIT(7));
+ data >>= 5;
+ if (!dp_link_is_bit_depth_valid(data)) {
+ pr_err("invalid link bit depth = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_bit_depth = data;
+ pr_debug("link bit depth = 0x%x (%s)\n",
+ link->request.test_bit_depth,
+ dp_link_bit_depth_to_string(link->request.test_bit_depth));
+
+ /* resolution timing params */
+ ret = dp_link_parse_timing_params1(link, 0x222, 2,
+ &link->request.test_h_total);
+ if (ret) {
+ pr_err("failed to parse test_h_total (0x222)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_TOTAL = %d\n", link->request.test_h_total);
+
+ ret = dp_link_parse_timing_params1(link, 0x224, 2,
+ &link->request.test_v_total);
+ if (ret) {
+ pr_err("failed to parse test_v_total (0x224)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_TOTAL = %d\n", link->request.test_v_total);
+
+ ret = dp_link_parse_timing_params1(link, 0x226, 2,
+ &link->request.test_h_start);
+ if (ret) {
+ pr_err("failed to parse test_h_start (0x226)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_START = %d\n", link->request.test_h_start);
+
+ ret = dp_link_parse_timing_params1(link, 0x228, 2,
+ &link->request.test_v_start);
+ if (ret) {
+ pr_err("failed to parse test_v_start (0x228)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_START = %d\n", link->request.test_v_start);
+
+ ret = dp_link_parse_timing_params2(link, 0x22A, 2,
+ &link->request.test_hsync_pol,
+ &link->request.test_hsync_width);
+ if (ret) {
+ pr_err("failed to parse (0x22A)\n");
+ goto exit;
+ }
+ pr_debug("TEST_HSYNC_POL = %d\n", link->request.test_hsync_pol);
+ pr_debug("TEST_HSYNC_WIDTH = %d\n", link->request.test_hsync_width);
+
+ ret = dp_link_parse_timing_params2(link, 0x22C, 2,
+ &link->request.test_vsync_pol,
+ &link->request.test_vsync_width);
+ if (ret) {
+ pr_err("failed to parse (0x22C)\n");
+ goto exit;
+ }
+ pr_debug("TEST_VSYNC_POL = %d\n", link->request.test_vsync_pol);
+ pr_debug("TEST_VSYNC_WIDTH = %d\n", link->request.test_vsync_width);
+
+ ret = dp_link_parse_timing_params1(link, 0x22E, 2,
+ &link->request.test_h_width);
+ if (ret) {
+ pr_err("failed to parse test_h_width (0x22E)\n");
+ goto exit;
+ }
+ pr_debug("TEST_H_WIDTH = %d\n", link->request.test_h_width);
+
+ ret = dp_link_parse_timing_params1(link, 0x230, 2,
+ &link->request.test_v_height);
+ if (ret) {
+ pr_err("failed to parse test_v_height (0x230)\n");
+ goto exit;
+ }
+ pr_debug("TEST_V_HEIGHT = %d\n", link->request.test_v_height);
+
+ ret = dp_link_parse_timing_params3(link, 0x233,
+ &link->request.test_rr_d);
+ link->request.test_rr_d &= BIT(0);
+ if (ret) {
+ pr_err("failed to parse test_rr_d (0x233)\n");
+ goto exit;
+ }
+ pr_debug("TEST_REFRESH_DENOMINATOR = %d\n", link->request.test_rr_d);
+
+ ret = dp_link_parse_timing_params3(link, 0x234,
+ &link->request.test_rr_n);
+ if (ret) {
+ pr_err("failed to parse test_rr_n (0x234)\n");
+ goto exit;
+ }
+ pr_debug("TEST_REFRESH_NUMERATOR = %d\n", link->request.test_rr_n);
+exit:
+ return ret;
+}
+
+/**
+ * dp_link_is_link_rate_valid() - validates the link rate
+ * @lane_rate: link rate requested by the sink
+ *
+ * Returns true if the requested link rate is supported.
+ */
+static bool dp_link_is_link_rate_valid(u32 link_rate)
+{
+ return ((link_rate == DP_LINK_RATE_162) ||
+ (link_rate == DP_LINK_RATE_270) ||
+ (link_rate == DP_LINK_RATE_540) ||
+ (link_rate == DP_LINK_RATE_810));
+}
+
+/**
+ * dp_link_is_lane_count_valid() - validates the lane count
+ * @lane_count: lane count requested by the sink
+ *
+ * Returns true if the requested lane count is supported.
+ */
+static bool dp_link_is_lane_count_valid(u32 lane_count)
+{
+ return (lane_count == DP_LANE_COUNT_1) ||
+ (lane_count == DP_LANE_COUNT_2) ||
+ (lane_count == DP_LANE_COUNT_4);
+}
+
+/**
+ * dp_link_parse_link_training_params() - parses link training parameters from
+ * DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane
+ * count (Byte 0x220), and if these values parse are valid.
+ */
+static int dp_link_parse_link_training_params(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int ret = 0;
+ int rlen;
+ int const param_len = 0x1;
+ int const test_link_rate_addr = 0x219;
+ int const test_lane_count_addr = 0x220;
+
+ /* Read the requested link rate (Byte 0x219). */
+ rlen = link->aux->read(link->aux, test_link_rate_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read link rate\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+
+ if (!dp_link_is_link_rate_valid(data)) {
+ pr_err("invalid link rate = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_link_rate = data;
+ pr_debug("link rate = 0x%x\n", link->request.test_link_rate);
+
+ /* Read the requested lane count (Byte 0x220). */
+ rlen = link->aux->read(link->aux, test_lane_count_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read lane count\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+ data = *bp;
+ data &= 0x1F;
+
+ if (!dp_link_is_lane_count_valid(data)) {
+ pr_err("invalid lane count = 0x%x\n", data);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ link->request.test_lane_count = data;
+ pr_debug("lane count = 0x%x\n", link->request.test_lane_count);
+exit:
+ return ret;
+}
+
+static bool dp_link_is_phy_test_pattern_supported(u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ case PHY_TEST_PATTERN_PRBS7:
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * dp_parse_phy_test_params() - parses the phy link parameters
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being
+ * requested.
+ */
+static int dp_link_parse_phy_test_params(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const phy_test_pattern_addr = 0x248;
+ int ret = 0;
+
+ rlen = link->aux->read(link->aux, phy_test_pattern_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read phy link pattern\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ link->request.phy_test_pattern_sel = data;
+
+ pr_debug("phy_test_pattern_sel = %s\n",
+ dp_link_get_phy_test_pattern(data));
+
+ if (!dp_link_is_phy_test_pattern_supported(data))
+ ret = -EINVAL;
+end:
+ return ret;
+}
+
+static char *dp_link_get_test_name(u32 test_requested)
+{
+ switch (test_requested) {
+ case TEST_LINK_TRAINING: return DP_LINK_ENUM_STR(TEST_LINK_TRAINING);
+ case TEST_VIDEO_PATTERN: return DP_LINK_ENUM_STR(TEST_VIDEO_PATTERN);
+ case PHY_TEST_PATTERN: return DP_LINK_ENUM_STR(PHY_TEST_PATTERN);
+ case TEST_EDID_READ: return DP_LINK_ENUM_STR(TEST_EDID_READ);
+ case TEST_AUDIO_PATTERN: return DP_LINK_ENUM_STR(TEST_AUDIO_PATTERN);
+ default: return "unknown";
+ }
+}
+
+/**
+ * dp_link_is_video_audio_test_requested() - checks for audio/video link request
+ * @link: link requested by the sink
+ *
+ * Returns true if the requested link is a permitted audio/video link.
+ */
+static bool dp_link_is_video_audio_test_requested(u32 link)
+{
+ return (link == TEST_VIDEO_PATTERN) ||
+ (link == (TEST_AUDIO_PATTERN | TEST_VIDEO_PATTERN)) ||
+ (link == TEST_AUDIO_PATTERN) ||
+ (link == (TEST_AUDIO_PATTERN | TEST_AUDIO_DISABLED_VIDEO));
+}
+
+/**
+ * dp_link_supported() - checks if link requested by sink is supported
+ * @test_requested: link requested by the sink
+ *
+ * Returns true if the requested link is supported.
+ */
+static bool dp_link_is_test_supported(u32 test_requested)
+{
+ return (test_requested == TEST_LINK_TRAINING) ||
+ (test_requested == TEST_EDID_READ) ||
+ (test_requested == PHY_TEST_PATTERN) ||
+ dp_link_is_video_audio_test_requested(test_requested);
+}
+
+/**
+ * dp_sink_parse_test_request() - parses link request parameters from sink
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD to check if an automated link is requested (Byte 0x201),
+ * and what type of link automation is being requested (Byte 0x218).
+ */
+static int dp_link_parse_request(struct dp_link_private *link)
+{
+ int ret = 0;
+ u8 *bp;
+ u8 data;
+ int rlen;
+ u32 const param_len = 0x1;
+ u32 const device_service_irq_addr = 0x201;
+ u32 const test_request_addr = 0x218;
+ u8 buf[4];
+
+ /**
+ * Read the device service IRQ vector (Byte 0x201) to determine
+ * whether an automated link has been requested by the sink.
+ */
+ rlen = link->aux->read(link->aux, device_service_irq_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("aux read failed\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("device service irq vector = 0x%x\n", data);
+
+ if (!(data & BIT(1))) {
+ pr_debug("no link requested\n");
+ goto end;
+ }
+
+ /**
+ * Read the link request byte (Byte 0x218) to determine what type
+ * of automated link has been requested by the sink.
+ */
+ rlen = link->aux->read(link->aux, test_request_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("aux read failed\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ if (!dp_link_is_test_supported(data)) {
+ pr_debug("link 0x%x not supported\n", data);
+ goto end;
+ }
+
+ pr_debug("%s (0x%x) requested\n", dp_link_get_test_name(data), data);
+ link->request.test_requested = data;
+
+ if (link->request.test_requested == PHY_TEST_PATTERN) {
+ ret = dp_link_parse_phy_test_params(link);
+ if (ret)
+ goto end;
+ ret = dp_link_parse_link_training_params(link);
+ }
+
+ if (link->request.test_requested == TEST_LINK_TRAINING)
+ ret = dp_link_parse_link_training_params(link);
+
+ if (dp_link_is_video_audio_test_requested(
+ link->request.test_requested)) {
+ ret = dp_link_parse_video_pattern_params(link);
+ if (ret)
+ goto end;
+
+ ret = dp_link_parse_audio_pattern_params(link);
+ }
+end:
+ /* clear the link request IRQ */
+ buf[0] = 1;
+ link->aux->write(link->aux, test_request_addr, 1, AUX_NATIVE, buf);
+
+ /**
+ * Send a TEST_ACK if all link parameters are valid, otherwise send
+ * a TEST_NACK.
+ */
+ if (ret)
+ link->request.response = TEST_NACK;
+ else
+ link->request.response = TEST_ACK;
+
+ return ret;
+}
+
+/**
+ * dp_link_parse_sink_count() - parses the sink count
+ *
+ * Parses the DPCD to check if there is an update to the sink count
+ * (Byte 0x200), and whether all the sink devices connected have Content
+ * Protection enabled.
+ */
+static void dp_link_parse_sink_count(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen;
+ int const param_len = 0x1;
+ int const sink_count_addr = 0x200;
+
+ rlen = link->aux->read(link->aux, sink_count_addr,
+ param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed to read sink count\n");
+ return;
+ }
+
+ data = *bp;
+
+ /* BIT 7, BIT 5:0 */
+ link->sink_count.count = (data & BIT(7)) << 6 | (data & 0x63);
+ /* BIT 6*/
+ link->sink_count.cp_ready = data & BIT(6);
+
+ pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n",
+ link->sink_count.count, link->sink_count.cp_ready);
+}
+
+static int dp_link_link_status_read(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int rlen, ret = 0;
+ int const addr = 0x202;
+ int const len = 6;
+ struct dp_link_status *sp;
+
+ rlen = link->aux->read(link->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen < len) {
+ pr_err("edp aux read failed\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ sp = &link->link_status;
+
+ data = *bp++; /* byte 0x202 */
+ sp->lane_01_status = data; /* lane 0, 1 */
+
+ data = *bp++; /* byte 0x203 */
+ sp->lane_23_status = data; /* lane 2, 3 */
+
+ data = *bp++; /* byte 0x204 */
+ sp->interlane_align_done = (data & BIT(0));
+ sp->downstream_port_status_changed = (data & BIT(6));
+ sp->link_status_updated = (data & BIT(7));
+
+ data = *bp++; /* byte 0x205 */
+ sp->port_0_in_sync = (data & BIT(0));
+ sp->port_1_in_sync = (data & BIT(1));
+
+ data = *bp++; /* byte 0x206 */
+ sp->req_voltage_swing[0] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[0] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[1] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[1] = data & 0x03;
+
+ data = *bp++; /* byte 0x207 */
+ sp->req_voltage_swing[2] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[2] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[3] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[3] = data & 0x03;
+
+ return 0;
+error:
+ return ret;
+}
+
+static void dp_link_parse_sink_status_field(struct dp_link_private *link)
+{
+ dp_link_parse_sink_count(link);
+ dp_link_parse_request(link);
+ dp_link_link_status_read(link);
+}
+
+static bool dp_link_is_link_training_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested == TEST_LINK_TRAINING);
+}
+
+/**
+ * dp_link_process_link_training_request() - processes new training requests
+ * @link: Display Port link data
+ *
+ * This function will handle new link training requests that are initiated by
+ * the sink. In particular, it will update the requested lane count and link
+ * link rate, and then trigger the link retraining procedure.
+ *
+ * The function will return 0 if a link training request has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_training_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_link_training_requested(link))
+ return -EINVAL;
+
+ pr_debug("%s link rate = 0x%x, lane count = 0x%x\n",
+ dp_link_get_test_name(TEST_LINK_TRAINING),
+ link->request.test_link_rate,
+ link->request.test_lane_count);
+
+ link->dp_link.lane_count = link->request.test_lane_count;
+ link->dp_link.link_rate = link->request.test_link_rate;
+
+ return 0;
+}
+
+static bool dp_link_phy_pattern_requested(struct dp_link *dp_link)
+{
+ struct dp_link_private *link = container_of(dp_link,
+ struct dp_link_private, dp_link);
+
+ return (link->request.test_requested == PHY_TEST_PATTERN);
+}
+
+static int dp_link_parse_vx_px(struct dp_link_private *link)
+{
+ u8 *bp;
+ u8 data;
+ int const param_len = 0x1;
+ int const addr1 = 0x206;
+ int const addr2 = 0x207;
+ int ret = 0;
+ u32 v0, p0, v1, p1, v2, p2, v3, p3;
+ int rlen;
+
+ pr_debug("\n");
+
+ rlen = link->aux->read(link->aux, addr1, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed reading lanes 0/1\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("lanes 0/1 (Byte 0x206): 0x%x\n", data);
+
+ v0 = data & 0x3;
+ data = data >> 2;
+ p0 = data & 0x3;
+ data = data >> 2;
+
+ v1 = data & 0x3;
+ data = data >> 2;
+ p1 = data & 0x3;
+ data = data >> 2;
+
+ rlen = link->aux->read(link->aux, addr2, param_len, AUX_NATIVE, &bp);
+ if (rlen < param_len) {
+ pr_err("failed reading lanes 2/3\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ data = *bp;
+
+ pr_debug("lanes 2/3 (Byte 0x207): 0x%x\n", data);
+
+ v2 = data & 0x3;
+ data = data >> 2;
+ p2 = data & 0x3;
+ data = data >> 2;
+
+ v3 = data & 0x3;
+ data = data >> 2;
+ p3 = data & 0x3;
+ data = data >> 2;
+
+ pr_debug("vx: 0=%d, 1=%d, 2=%d, 3=%d\n", v0, v1, v2, v3);
+ pr_debug("px: 0=%d, 1=%d, 2=%d, 3=%d\n", p0, p1, p2, p3);
+
+ /**
+ * Update the voltage and pre-emphasis levels as per DPCD request
+ * vector.
+ */
+ pr_debug("Current: v_level = 0x%x, p_level = 0x%x\n",
+ link->dp_link.v_level, link->dp_link.p_level);
+ pr_debug("Requested: v_level = 0x%x, p_level = 0x%x\n", v0, p0);
+ link->dp_link.v_level = v0;
+ link->dp_link.p_level = p0;
+
+ pr_debug("Success\n");
+end:
+ return ret;
+}
+
+/**
+ * dp_link_process_phy_test_pattern_request() - process new phy link requests
+ * @link: Display Port Driver data
+ *
+ * This function will handle new phy link pattern requests that are initiated
+ * by the sink. The function will return 0 if a phy link pattern has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_phy_test_pattern_request(
+ struct dp_link_private *link)
+{
+ u32 test_link_rate = 0, test_lane_count = 0;
+
+ if (!dp_link_phy_pattern_requested(&link->dp_link))
+ return -EINVAL;
+
+ test_link_rate = link->request.test_link_rate;
+ test_lane_count = link->request.test_lane_count;
+
+ if (!dp_link_is_link_rate_valid(test_link_rate) ||
+ !dp_link_is_lane_count_valid(test_lane_count)) {
+ pr_err("Invalid params: link rate = 0x%x, lane count = 0x%x\n",
+ test_link_rate, test_lane_count);
+ return -EINVAL;
+ }
+
+ pr_debug("start\n");
+
+ link->dp_link.lane_count = link->request.test_lane_count;
+ link->dp_link.link_rate = link->request.test_link_rate;
+
+ dp_link_parse_vx_px(link);
+
+ pr_debug("end\n");
+
+ return 0;
+}
+
+static bool dp_link_is_link_status_updated(struct dp_link_private *link)
+{
+ return link->link_status.link_status_updated;
+}
+
+static bool dp_link_channel_eq_done(struct dp_link_private *link)
+{
+ u32 mask, data;
+ struct dp_link *dp_link = &link->dp_link;
+
+ pr_debug("\n");
+
+ dp_link_link_status_read(link);
+
+ if (!link->link_status.interlane_align_done) { /* not align */
+ pr_err("interlane align failed\n");
+ return 0;
+ }
+
+ if (dp_link->lane_count == 1) {
+ mask = 0x7;
+ data = link->link_status.lane_01_status;
+ } else if (dp_link->lane_count == 2) {
+ mask = 0x77;
+ data = link->link_status.lane_01_status;
+ } else {
+ mask = 0x7777;
+ data = link->link_status.lane_23_status;
+ data <<= 8;
+ data |= link->link_status.lane_01_status;
+ }
+
+ data &= mask;
+ pr_debug("data=%x mask=%x\n", data, mask);
+
+ if (data == mask)/* all done */
+ return true;
+
+ return false;
+}
+
+static bool dp_link_clock_recovery_done(struct dp_link_private *link)
+{
+ u32 mask, data;
+ struct dp_link *dp_link = &link->dp_link;
+
+ dp_link_link_status_read(link);
+
+ if (dp_link->lane_count == 1) {
+ mask = 0x01; /* lane 0 */
+ data = link->link_status.lane_01_status;
+ } else if (dp_link->lane_count == 2) {
+ mask = 0x011; /*B lane 0, 1 */
+ data = link->link_status.lane_01_status;
+ } else {
+ mask = 0x01111; /*B lane 0, 1 */
+ data = link->link_status.lane_23_status;
+ data <<= 8;
+ data |= link->link_status.lane_01_status;
+ }
+
+ data &= mask;
+ pr_debug("data=%x mask=%x\n", data, mask);
+
+ if (data == mask) /* all done */
+ return true;
+
+ return false;
+}
+
+/**
+ * dp_link_process_link_status_update() - processes link status updates
+ * @link: Display Port link module data
+ *
+ * This function will check for changes in the link status, e.g. clock
+ * recovery done on all lanes, and trigger link training if there is a
+ * failure/error on the link.
+ *
+ * The function will return 0 if the a link status update has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_status_update(struct dp_link_private *link)
+{
+ if (!dp_link_is_link_status_updated(link) ||
+ (dp_link_channel_eq_done(link) &&
+ dp_link_clock_recovery_done(link)))
+ return -EINVAL;
+
+ pr_debug("channel_eq_done = %d, clock_recovery_done = %d\n",
+ dp_link_channel_eq_done(link),
+ dp_link_clock_recovery_done(link));
+
+ return 0;
+}
+
+static bool dp_link_is_ds_port_status_changed(struct dp_link_private *link)
+{
+ return link->link_status.downstream_port_status_changed;
+}
+
+/**
+ * dp_link_process_downstream_port_status_change() - process port status changes
+ * @link: Display Port Driver data
+ *
+ * This function will handle downstream port updates that are initiated by
+ * the sink. If the downstream port status has changed, the EDID is read via
+ * AUX.
+ *
+ * The function will return 0 if a downstream port update has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
+{
+ if (!dp_link_is_ds_port_status_changed(link))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool dp_link_is_video_pattern_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested & TEST_VIDEO_PATTERN)
+ && !(link->request.test_requested & TEST_AUDIO_DISABLED_VIDEO);
+}
+
+static bool dp_link_is_audio_pattern_requested(struct dp_link_private *link)
+{
+ return (link->request.test_requested & TEST_AUDIO_PATTERN);
+}
+
+/**
+ * dp_link_process_video_pattern_request() - process new video pattern request
+ * @link: Display Port link module's data
+ *
+ * This function will handle a new video pattern request that are initiated by
+ * the sink. This is acheieved by first sending a disconnect notification to
+ * the sink followed by a subsequent connect notification to the user modules,
+ * where it is expected that the user modules would draw the required link
+ * pattern.
+ */
+static int dp_link_process_video_pattern_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_video_pattern_requested(link))
+ goto end;
+
+ pr_debug("%s: bit depth=%d(%d bpp) pattern=%s\n",
+ dp_link_get_test_name(TEST_VIDEO_PATTERN),
+ link->request.test_bit_depth,
+ dp_link_bit_depth_to_bpp(link->request.test_bit_depth),
+ dp_link_video_pattern_to_string(
+ link->request.test_video_pattern));
+
+ return 0;
+end:
+ return -EINVAL;
+}
+
+/**
+ * dp_link_process_audio_pattern_request() - process new audio pattern request
+ * @link: Display Port link module data
+ *
+ * This function will handle a new audio pattern request that is initiated by
+ * the sink. This is acheieved by sending the necessary secondary data packets
+ * to the sink. It is expected that any simulatenous requests for video
+ * patterns will be handled before the audio pattern is sent to the sink.
+ */
+static int dp_link_process_audio_pattern_request(struct dp_link_private *link)
+{
+ if (!dp_link_is_audio_pattern_requested(link))
+ return -EINVAL;
+
+ pr_debug("sampling_rate=%s, channel_count=%d, pattern_type=%s\n",
+ dp_link_get_audio_sample_rate(
+ link->request.test_audio_sampling_rate),
+ link->request.test_audio_channel_count,
+ dp_link_get_audio_test_pattern(
+ link->request.test_audio_pattern_type));
+
+ pr_debug("audio_period: ch1=0x%x, ch2=0x%x, ch3=0x%x, ch4=0x%x\n",
+ link->request.test_audio_period_ch_1,
+ link->request.test_audio_period_ch_2,
+ link->request.test_audio_period_ch_3,
+ link->request.test_audio_period_ch_4);
+
+ pr_debug("audio_period: ch5=0x%x, ch6=0x%x, ch7=0x%x, ch8=0x%x\n",
+ link->request.test_audio_period_ch_5,
+ link->request.test_audio_period_ch_6,
+ link->request.test_audio_period_ch_7,
+ link->request.test_audio_period_ch_8);
+
+ return 0;
+}
+
+static void dp_link_reset_data(struct dp_link_private *link)
+{
+ link->request = (const struct dp_link_request){ 0 };
+ link->request.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
+
+ link->dp_link.test_requested = 0;
+}
+
+/**
+ * dp_link_process_request() - handle HPD IRQ transition to HIGH
+ * @link: pointer to link module data
+ *
+ * This function will handle the HPD IRQ state transitions from LOW to HIGH
+ * (including cases when there are back to back HPD IRQ HIGH) indicating
+ * the start of a new link training request or sink status update.
+ */
+static int dp_link_process_request(struct dp_link *dp_link)
+{
+ int ret = 0;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ pr_debug("start\n");
+
+ dp_link_reset_data(link);
+
+ dp_link_parse_sink_status_field(link);
+
+ ret = dp_link_process_link_training_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_LINK_TRAINING;
+ goto exit;
+ }
+
+ ret = dp_link_process_phy_test_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= PHY_TEST_PATTERN;
+ goto exit;
+ }
+
+ ret = dp_link_process_link_status_update(link);
+ if (!ret) {
+ dp_link->test_requested |= LINK_STATUS_UPDATED;
+ goto exit;
+ }
+
+ ret = dp_link_process_ds_port_status_change(link);
+ if (!ret) {
+ dp_link->test_requested |= DS_PORT_STATUS_CHANGED;
+ goto exit;
+ }
+
+ ret = dp_link_process_video_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_VIDEO_PATTERN;
+ goto exit;
+ }
+
+ ret = dp_link_process_audio_pattern_request(link);
+ if (!ret) {
+ dp_link->test_requested |= TEST_AUDIO_PATTERN;
+ goto exit;
+ }
+
+ pr_debug("done\n");
+exit:
+ return ret;
+}
+
+static u8 *dp_link_get_voltage_swing(struct dp_link *dp_link)
+
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return link->link_status.req_voltage_swing;
+}
+
+static u8 *dp_link_get_pre_emphasis(struct dp_link *dp_link)
+
+{
+ struct dp_link_private *link;
+
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return link->link_status.req_pre_emphasis;
+}
+
+static int dp_link_get_colorimetry_config(struct dp_link *dp_link)
+{
+ u32 cc;
+ enum dynamic_range dr;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ /* unless a video pattern CTS test is ongoing, use CEA_VESA */
+ if (dp_link_is_video_pattern_requested(link))
+ dr = link->request.test_dyn_range;
+ else
+ dr = DP_DYNAMIC_RANGE_RGB_VESA;
+
+ /* Only RGB_VESA nd RGB_CEA supported for now */
+ switch (dr) {
+ case DP_DYNAMIC_RANGE_RGB_CEA:
+ cc = BIT(3);
+ break;
+ case DP_DYNAMIC_RANGE_RGB_VESA:
+ default:
+ cc = 0;
+ }
+
+ return cc;
+}
+
+static bool dp_link_clock_recovery(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return dp_link_clock_recovery_done(link);
+}
+
+static bool dp_link_channel_equalization(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return dp_link_channel_eq_done(link);
+}
+
+static int dp_link_adjust_levels(struct dp_link *dp_link)
+{
+ int i;
+ int max = 0;
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ /* use the max level across lanes */
+ for (i = 0; i < dp_link->lane_count; i++) {
+ pr_debug("lane=%d req_voltage_swing=%d\n",
+ i, link->link_status.req_voltage_swing[i]);
+ if (max < link->link_status.req_voltage_swing[i])
+ max = link->link_status.req_voltage_swing[i];
+ }
+
+ dp_link->v_level = max;
+
+ /* use the max level across lanes */
+ max = 0;
+ for (i = 0; i < dp_link->lane_count; i++) {
+ pr_debug("lane=%d req_pre_emphasis=%d\n",
+ i, link->link_status.req_pre_emphasis[i]);
+ if (max < link->link_status.req_pre_emphasis[i])
+ max = link->link_status.req_pre_emphasis[i];
+ }
+
+ dp_link->p_level = max;
+
+ /**
+ * Adjust the voltage swing and pre-emphasis level combination to within
+ * the allowable range.
+ */
+ if (dp_link->v_level > DP_LINK_VOLTAGE_MAX) {
+ pr_debug("Requested vSwingLevel=%d, change to %d\n",
+ dp_link->v_level, DP_LINK_VOLTAGE_MAX);
+ dp_link->v_level = DP_LINK_VOLTAGE_MAX;
+ }
+
+ if (dp_link->p_level > DP_LINK_PRE_EMPHASIS_MAX) {
+ pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+ dp_link->p_level, DP_LINK_PRE_EMPHASIS_MAX);
+ dp_link->p_level = DP_LINK_PRE_EMPHASIS_MAX;
+ }
+
+ if ((dp_link->p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
+ && (dp_link->v_level == DP_LINK_VOLTAGE_LEVEL_2)) {
+ pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+ dp_link->p_level, DP_LINK_PRE_EMPHASIS_LEVEL_1);
+ dp_link->p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
+ }
+
+ pr_debug("v_level=%d, p_level=%d\n",
+ dp_link->v_level, dp_link->p_level);
+
+ return 0;
+}
+
+static int dp_link_send_psm_request(struct dp_link *dp_link, bool req)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ return 0;
+}
+
+static u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp)
+{
+ enum test_bit_depth tbd;
+
+ /*
+ * Few simplistic rules and assumptions made here:
+ * 1. Test bit depth is bit depth per color component
+ * 2. Assume 3 color components
+ */
+ switch (bpp) {
+ case 18:
+ tbd = DP_TEST_BIT_DEPTH_6;
+ break;
+ case 24:
+ tbd = DP_TEST_BIT_DEPTH_8;
+ break;
+ case 30:
+ tbd = DP_TEST_BIT_DEPTH_10;
+ break;
+ default:
+ tbd = DP_TEST_BIT_DEPTH_UNKNOWN;
+ break;
+ }
+
+ return tbd;
+}
+
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux)
+{
+ int rc = 0;
+ struct dp_link_private *link;
+ struct dp_link *dp_link;
+
+ if (!dev || !aux) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link->dev = dev;
+ link->aux = aux;
+
+ dp_link = &link->dp_link;
+
+ dp_link->process_request = dp_link_process_request;
+ dp_link->get_voltage_swing = dp_link_get_voltage_swing;
+ dp_link->get_test_bits_depth = dp_link_get_test_bits_depth;
+ dp_link->get_pre_emphasis = dp_link_get_pre_emphasis;
+ dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
+ dp_link->clock_recovery = dp_link_clock_recovery;
+ dp_link->channel_equalization = dp_link_channel_equalization;
+ dp_link->adjust_levels = dp_link_adjust_levels;
+ dp_link->send_psm_request = dp_link_send_psm_request;
+ dp_link->phy_pattern_requested = dp_link_phy_pattern_requested;
+
+ return dp_link;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_link_put(struct dp_link *dp_link)
+{
+ struct dp_link_private *link;
+
+ if (!dp_link)
+ return;
+
+ link = container_of(dp_link, struct dp_link_private, dp_link);
+
+ devm_kfree(link->dev, link);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
new file mode 100644
index 0000000..de10e9a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_LINK_H_
+#define _DP_LINK_H_
+
+#include "dp_aux.h"
+
+enum dp_link_voltage_level {
+ DP_LINK_VOLTAGE_LEVEL_0 = 0,
+ DP_LINK_VOLTAGE_LEVEL_1 = 1,
+ DP_LINK_VOLTAGE_LEVEL_2 = 2,
+ DP_LINK_VOLTAGE_MAX = DP_LINK_VOLTAGE_LEVEL_2,
+};
+
+enum dp_link_preemaphasis_level {
+ DP_LINK_PRE_EMPHASIS_LEVEL_0 = 0,
+ DP_LINK_PRE_EMPHASIS_LEVEL_1 = 1,
+ DP_LINK_PRE_EMPHASIS_LEVEL_2 = 2,
+ DP_LINK_PRE_EMPHASIS_MAX = DP_LINK_PRE_EMPHASIS_LEVEL_2,
+};
+
+enum test_type {
+ UNKNOWN_TEST = 0,
+ TEST_LINK_TRAINING = 0x01,
+ TEST_VIDEO_PATTERN = 0x02,
+ PHY_TEST_PATTERN = 0x08,
+ TEST_EDID_READ = 0x04,
+ TEST_AUDIO_PATTERN = 0x20,
+ TEST_AUDIO_DISABLED_VIDEO = 0x40,
+};
+
+enum status_update {
+ LINK_STATUS_UPDATED = 0x100,
+ DS_PORT_STATUS_CHANGED = 0x200,
+};
+
+struct dp_link {
+ u32 test_requested;
+
+ u32 lane_count;
+ u32 link_rate;
+ u32 v_level;
+ u32 p_level;
+
+ u8 *(*get_voltage_swing)(struct dp_link *dp_link);
+ u8 *(*get_pre_emphasis)(struct dp_link *dp_link);
+ u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
+ int (*process_request)(struct dp_link *dp_link);
+ int (*get_colorimetry_config)(struct dp_link *dp_link);
+ int (*adjust_levels)(struct dp_link *dp_link);
+ int (*send_psm_request)(struct dp_link *dp_link, bool req);
+ bool (*clock_recovery)(struct dp_link *dp_link);
+ bool (*channel_equalization)(struct dp_link *dp_link);
+ bool (*phy_pattern_requested)(struct dp_link *dp_link);
+};
+
+/**
+ * dp_link_get() - get the functionalities of dp test module
+ *
+ *
+ * return: a pointer to dp_link struct
+ */
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux);
+
+/**
+ * dp_link_put() - releases the dp test module's resources
+ *
+ * @dp_link: an instance of dp_link module
+ *
+ */
+void dp_link_put(struct dp_link *dp_link);
+
+#endif /* _DP_LINK_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
new file mode 100644
index 0000000..f9616c4
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include "dp_panel.h"
+
+#define DP_LINK_RATE_MULTIPLIER 27000000
+
+struct dp_panel_private {
+ struct device *dev;
+ struct dp_panel dp_panel;
+ struct dp_aux *aux;
+ struct dp_catalog_panel *catalog;
+};
+
+static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
+{
+ u8 *bp;
+ u8 data;
+ u32 const addr = 0x0;
+ u32 const len = 16;
+ int rlen, rc = 0;
+ struct dp_panel_private *panel;
+ struct dp_panel_dpcd *cap;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ cap = &dp_panel->dpcd;
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ rlen = panel->aux->read(panel->aux, addr, len, AUX_NATIVE, &bp);
+ if (rlen != len) {
+ pr_err("dpcd read failed, rlen=%d\n", rlen);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ memset(cap, 0, sizeof(*cap));
+
+ data = *bp++; /* byte 0 */
+ cap->major = (data >> 4) & 0x0f;
+ cap->minor = data & 0x0f;
+ pr_debug("version: %d.%d\n", cap->major, cap->minor);
+
+ data = *bp++; /* byte 1 */
+ /* 162, 270, 540, 810 MB, symbol rate, NOT bit rate */
+ cap->max_link_rate = data;
+ pr_debug("link_rate=%d\n", cap->max_link_rate);
+
+ data = *bp++; /* byte 2 */
+ if (data & BIT(7))
+ cap->enhanced_frame++;
+
+ if (data & 0x40) {
+ cap->flags |= DPCD_TPS3;
+ pr_debug("pattern 3 supported\n");
+ } else {
+ pr_debug("pattern 3 not supported\n");
+ }
+
+ data &= 0x0f;
+ cap->max_lane_count = data;
+ pr_debug("lane_count=%d\n", cap->max_lane_count);
+
+ data = *bp++; /* byte 3 */
+ if (data & BIT(0)) {
+ cap->flags |= DPCD_MAX_DOWNSPREAD_0_5;
+ pr_debug("max_downspread\n");
+ }
+
+ if (data & BIT(6)) {
+ cap->flags |= DPCD_NO_AUX_HANDSHAKE;
+ pr_debug("NO Link Training\n");
+ }
+
+ data = *bp++; /* byte 4 */
+ cap->num_rx_port = (data & BIT(0)) + 1;
+ pr_debug("rx_ports=%d", cap->num_rx_port);
+
+ data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
+ cap->downstream_port.dfp_present = data & BIT(0);
+ cap->downstream_port.dfp_type = data & 0x6;
+ cap->downstream_port.format_conversion = data & BIT(3);
+ cap->downstream_port.detailed_cap_info_available = data & BIT(4);
+ pr_debug("dfp_present = %d, dfp_type = %d\n",
+ cap->downstream_port.dfp_present,
+ cap->downstream_port.dfp_type);
+ pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
+ cap->downstream_port.format_conversion,
+ cap->downstream_port.detailed_cap_info_available);
+
+ bp += 1; /* Skip Byte 6 */
+ rlen -= 1;
+
+ data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
+ cap->downstream_port.dfp_count = data & 0x7;
+ cap->downstream_port.msa_timing_par_ignored = data & BIT(6);
+ cap->downstream_port.oui_support = data & BIT(7);
+ pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n",
+ cap->downstream_port.dfp_count,
+ cap->downstream_port.msa_timing_par_ignored);
+ pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
+
+ data = *bp++; /* byte 8 */
+ if (data & BIT(1)) {
+ cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
+ pr_debug("edid presented\n");
+ }
+
+ data = *bp++; /* byte 9 */
+ cap->rx_port0_buf_size = (data + 1) * 32;
+ pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
+
+ bp += 2; /* skip 10, 11 port1 capability */
+ rlen -= 2;
+
+ data = *bp++; /* byte 12 */
+ cap->i2c_speed_ctrl = data;
+ if (cap->i2c_speed_ctrl > 0)
+ pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl);
+
+ data = *bp++; /* byte 13 */
+ cap->scrambler_reset = data & BIT(0);
+ pr_debug("scrambler_reset=%d\n", cap->scrambler_reset);
+
+ if (data & BIT(1))
+ cap->enhanced_frame++;
+
+ pr_debug("enhanced_framing=%d\n", cap->enhanced_frame);
+
+ data = *bp++; /* byte 14 */
+ if (data == 0)
+ cap->training_read_interval = 4000; /* us */
+ else
+ cap->training_read_interval = 4000 * data; /* us */
+ pr_debug("training_interval=%d\n", cap->training_read_interval);
+end:
+ return rc;
+}
+
+/*
+ * edid standard header bytes
+ */
+static u8 edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+
+static bool dp_panel_is_edid_header_valid(u8 *buf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_hdr); i++) {
+ if (buf[i] != edid_hdr[i])
+ return false;
+ }
+
+ return true;
+}
+
+static int dp_panel_validate_edid(u8 *bp, int len)
+{
+ int i;
+ u8 csum = 0;
+ u32 const size = 128;
+
+ if (len < size) {
+ pr_err("Error: len=%x\n", len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i++)
+ csum += *bp++;
+
+ if (csum != 0) {
+ pr_err("error: csum=0x%x\n", csum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dp_panel_read_edid(struct dp_panel *dp_panel)
+{
+ u8 *edid_buf;
+ u32 checksum = 0;
+ int rlen, ret = 0;
+ int edid_blk = 0, blk_num = 0, retries = 10;
+ u32 const segment_addr = 0x30;
+ bool edid_parsing_done = false;
+ struct dp_panel_private *panel;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ ret = panel->aux->ready(panel->aux);
+ if (!ret) {
+ pr_err("aux chan NOT ready\n");
+ goto end;
+ }
+
+ do {
+ u8 segment;
+
+
+ /*
+ * Write the segment first.
+ * Segment = 0, for blocks 0 and 1
+ * Segment = 1, for blocks 2 and 3
+ * Segment = 2, for blocks 3 and 4
+ * and so on ...
+ */
+ segment = blk_num >> 1;
+
+ panel->aux->write(panel->aux, segment_addr, 1, AUX_I2C,
+ &segment);
+
+ rlen = panel->aux->read(panel->aux, EDID_START_ADDRESS +
+ (blk_num * EDID_BLOCK_SIZE),
+ EDID_BLOCK_SIZE, AUX_I2C, &edid_buf);
+ if (rlen != EDID_BLOCK_SIZE) {
+ pr_err("invalid edid len: %d\n", rlen);
+ continue;
+ }
+
+ pr_debug("=== EDID data ===\n");
+ print_hex_dump(KERN_DEBUG, "EDID: ", DUMP_PREFIX_NONE, 16, 1,
+ edid_buf, EDID_BLOCK_SIZE, false);
+
+ pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen);
+
+ if (dp_panel_is_edid_header_valid(edid_buf)) {
+ ret = dp_panel_validate_edid(edid_buf, rlen);
+ if (ret) {
+ pr_err("corrupt edid block detected\n");
+ goto end;
+ }
+
+ if (edid_parsing_done) {
+ blk_num++;
+ continue;
+ }
+
+ dp_panel->edid.ext_block_cnt = edid_buf[0x7E];
+ edid_parsing_done = true;
+ checksum = edid_buf[rlen - 1];
+ } else {
+ edid_blk++;
+ blk_num++;
+ }
+
+ memcpy(dp_panel->edid.buf + (edid_blk * EDID_BLOCK_SIZE),
+ edid_buf, EDID_BLOCK_SIZE);
+
+ if (edid_blk == dp_panel->edid.ext_block_cnt)
+ goto end;
+ } while (retries--);
+end:
+ return ret;
+}
+
+static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
+{
+ int rc = 0;
+ u32 data, total_ver, total_hor;
+ struct dp_catalog_panel *catalog;
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ catalog = panel->catalog;
+ pinfo = &panel->dp_panel.pinfo;
+
+ pr_debug("width=%d hporch= %d %d %d\n",
+ pinfo->h_active, pinfo->h_back_porch,
+ pinfo->h_front_porch, pinfo->h_sync_width);
+
+ pr_debug("height=%d vporch= %d %d %d\n",
+ pinfo->v_active, pinfo->v_back_porch,
+ pinfo->v_front_porch, pinfo->v_sync_width);
+
+ total_hor = pinfo->h_active + pinfo->h_back_porch +
+ pinfo->h_front_porch + pinfo->h_sync_width;
+
+ total_ver = pinfo->v_active + pinfo->v_back_porch +
+ pinfo->v_front_porch + pinfo->v_sync_width;
+
+ data = total_ver;
+ data <<= 16;
+ data |= total_hor;
+
+ catalog->total = data;
+
+ data = (pinfo->v_back_porch + pinfo->v_sync_width);
+ data <<= 16;
+ data |= (pinfo->h_back_porch + pinfo->h_sync_width);
+
+ catalog->sync_start = data;
+
+ data = pinfo->v_sync_width;
+ data <<= 16;
+ data |= (pinfo->v_active_low << 31);
+ data |= pinfo->h_sync_width;
+ data |= (pinfo->h_active_low << 15);
+
+ catalog->width_blanking = data;
+
+ data = pinfo->v_active;
+ data <<= 16;
+ data |= pinfo->h_active;
+
+ catalog->dp_active = data;
+
+ panel->catalog->timing_cfg(catalog);
+end:
+ return rc;
+}
+
+static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
+{
+ int rc = 0;
+ struct dp_panel_private *panel;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+end:
+ return rc;
+}
+
+static u8 dp_panel_get_link_rate(struct dp_panel *dp_panel)
+{
+ const u32 encoding_factx10 = 8;
+ const u32 ln_to_link_ratio = 10;
+ u32 min_link_rate, reminder = 0;
+ u8 calc_link_rate = 0, lane_cnt;
+ struct dp_panel_private *panel;
+ struct dp_panel_info *pinfo;
+
+ if (!dp_panel) {
+ pr_err("invalid input\n");
+ goto end;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ lane_cnt = dp_panel->dpcd.max_lane_count;
+ pinfo = &dp_panel->pinfo;
+
+ pinfo->bpp = 24;
+
+ /*
+ * The max pixel clock supported is 675Mhz. The
+ * current calculations below will make sure
+ * the min_link_rate is within 32 bit limits.
+ * Any changes in the section of code should
+ * consider this limitation.
+ */
+ min_link_rate = (u32)div_u64(pinfo->pixel_clk_khz * 1000,
+ (lane_cnt * encoding_factx10));
+ min_link_rate /= ln_to_link_ratio;
+ min_link_rate = (min_link_rate * pinfo->bpp);
+ min_link_rate = (u32)div_u64_rem(min_link_rate * 10,
+ DP_LINK_RATE_MULTIPLIER, &reminder);
+
+ /*
+ * To avoid any fractional values,
+ * increment the min_link_rate
+ */
+ if (reminder)
+ min_link_rate += 1;
+ pr_debug("min_link_rate = %d\n", min_link_rate);
+
+ if (min_link_rate <= DP_LINK_RATE_162)
+ calc_link_rate = DP_LINK_RATE_162;
+ else if (min_link_rate <= DP_LINK_RATE_270)
+ calc_link_rate = DP_LINK_RATE_270;
+ else if (min_link_rate <= DP_LINK_RATE_540)
+ calc_link_rate = DP_LINK_RATE_540;
+ else if (min_link_rate <= DP_LINK_RATE_810)
+ calc_link_rate = DP_LINK_RATE_810;
+ else {
+ /* Cap the link rate to the max supported rate */
+ pr_debug("link_rate = %d is unsupported\n", min_link_rate);
+ calc_link_rate = DP_LINK_RATE_810;
+ }
+
+ if (calc_link_rate > dp_panel->dpcd.max_link_rate)
+ calc_link_rate = dp_panel->dpcd.max_link_rate;
+
+ pr_debug("calc_link_rate = 0x%x\n", calc_link_rate);
+end:
+ return calc_link_rate;
+}
+
+struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
+ struct dp_catalog_panel *catalog)
+{
+ int rc = 0;
+ struct dp_panel_private *panel;
+ struct dp_panel *dp_panel;
+
+ if (!dev || !aux || !catalog) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ panel->dev = dev;
+ panel->aux = aux;
+ panel->catalog = catalog;
+
+ dp_panel = &panel->dp_panel;
+
+ dp_panel->edid.buf = devm_kzalloc(dev,
+ sizeof(EDID_BLOCK_SIZE) * 4, GFP_KERNEL);
+
+ dp_panel->init_info = dp_panel_init_panel_info;
+ dp_panel->timing_cfg = dp_panel_timing_cfg;
+ dp_panel->read_edid = dp_panel_read_edid;
+ dp_panel->read_dpcd = dp_panel_read_dpcd;
+ dp_panel->get_link_rate = dp_panel_get_link_rate;
+
+ return dp_panel;
+error:
+ return ERR_PTR(rc);
+}
+
+void dp_panel_put(struct dp_panel *dp_panel)
+{
+ struct dp_panel_private *panel;
+
+ if (!dp_panel)
+ return;
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+ devm_kfree(panel->dev, dp_panel->edid.buf);
+ devm_kfree(panel->dev, panel);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
new file mode 100644
index 0000000..5c145eb
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_PANEL_H_
+#define _DP_PANEL_H_
+
+#include "dp_aux.h"
+
+#define DPCD_ENHANCED_FRAME BIT(0)
+#define DPCD_TPS3 BIT(1)
+#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
+#define DPCD_NO_AUX_HANDSHAKE BIT(3)
+#define DPCD_PORT_0_EDID_PRESENTED BIT(4)
+
+#define EDID_START_ADDRESS 0x50
+#define EDID_BLOCK_SIZE 0x80
+
+
+#define DP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */
+#define DP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */
+#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */
+#define DP_LINK_RATE_810 30 /* 8.10G = 270M * 30 */
+#define DP_LINK_RATE_MAX DP_LINK_RATE_810
+
+struct downstream_port_config {
+ /* Byte 02205h */
+ bool dfp_present;
+ u32 dfp_type;
+ bool format_conversion;
+ bool detailed_cap_info_available;
+ /* Byte 02207h */
+ u32 dfp_count;
+ bool msa_timing_par_ignored;
+ bool oui_support;
+};
+
+struct dp_panel_dpcd {
+ u8 major;
+ u8 minor;
+ u8 max_lane_count;
+ u8 num_rx_port;
+ u8 i2c_speed_ctrl;
+ u8 scrambler_reset;
+ u8 enhanced_frame;
+ u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */
+ u32 flags;
+ u32 rx_port0_buf_size;
+ u32 training_read_interval;/* us */
+ struct downstream_port_config downstream_port;
+};
+
+struct dp_panel_edid {
+ u8 *buf;
+ u8 id_name[4];
+ u8 id_product;
+ u8 version;
+ u8 revision;
+ u8 video_intf; /* dp == 0x5 */
+ u8 color_depth; /* 6, 8, 10, 12 and 14 bits */
+ u8 color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+ u8 dpm; /* display power management */
+ u8 sync_digital; /* 1 = digital */
+ u8 sync_separate; /* 1 = separate */
+ u8 vsync_pol; /* 0 = negative, 1 = positive */
+ u8 hsync_pol; /* 0 = negative, 1 = positive */
+ u8 ext_block_cnt;
+};
+
+struct dp_panel_info {
+ u32 h_active;
+ u32 v_active;
+ u32 h_back_porch;
+ u32 h_front_porch;
+ u32 h_sync_width;
+ u32 h_active_low;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 v_sync_width;
+ u32 v_active_low;
+ u32 h_skew;
+ u32 refresh_rate;
+ u32 pixel_clk_khz;
+ u32 bpp;
+};
+
+struct dp_panel {
+ struct dp_panel_dpcd dpcd;
+ struct dp_panel_edid edid;
+ struct dp_panel_info pinfo;
+
+ u32 vic;
+
+ int (*init_info)(struct dp_panel *dp_panel);
+ int (*timing_cfg)(struct dp_panel *dp_panel);
+ int (*read_edid)(struct dp_panel *dp_panel);
+ int (*read_dpcd)(struct dp_panel *dp_panel);
+ u8 (*get_link_rate)(struct dp_panel *dp_panel);
+};
+
+struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
+ struct dp_catalog_panel *catalog);
+void dp_panel_put(struct dp_panel *dp_panel);
+#endif /* _DP_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 5adef2d..1b40161 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -428,6 +428,103 @@
sde_kms->irq_obj.total_irqs = 0;
}
+static void sde_core_irq_mask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != NULL);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ /* memory barrier */
+ smp_mb__before_atomic();
+ clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ /* memory barrier */
+ smp_mb__after_atomic();
+}
+
+static void sde_core_irq_unmask(struct irq_data *irqd)
+{
+ struct sde_kms *sde_kms;
+
+ if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
+ SDE_ERROR("invalid parameters irqd %d\n", irqd != NULL);
+ return;
+ }
+ sde_kms = irq_data_get_irq_chip_data(irqd);
+
+ /* memory barrier */
+ smp_mb__before_atomic();
+ set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
+ /* memory barrier */
+ smp_mb__after_atomic();
+}
+
+static struct irq_chip sde_core_irq_chip = {
+ .name = "sde",
+ .irq_mask = sde_core_irq_mask,
+ .irq_unmask = sde_core_irq_unmask,
+};
+
+static int sde_core_irqdomain_map(struct irq_domain *domain,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ struct sde_kms *sde_kms;
+ int rc;
+
+ if (!domain || !domain->host_data) {
+ SDE_ERROR("invalid parameters domain %d\n", domain != NULL);
+ return -EINVAL;
+ }
+ sde_kms = domain->host_data;
+
+ irq_set_chip_and_handler(irq, &sde_core_irq_chip, handle_level_irq);
+ rc = irq_set_chip_data(irq, sde_kms);
+
+ return rc;
+}
+
+static const struct irq_domain_ops sde_core_irqdomain_ops = {
+ .map = sde_core_irqdomain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+int sde_core_irq_domain_add(struct sde_kms *sde_kms)
+{
+ struct device *dev;
+ struct irq_domain *domain;
+
+ if (!sde_kms->dev || !sde_kms->dev->dev) {
+ pr_err("invalid device handles\n");
+ return -EINVAL;
+ }
+
+ dev = sde_kms->dev->dev;
+
+ domain = irq_domain_add_linear(dev->of_node, 32,
+ &sde_core_irqdomain_ops, sde_kms);
+ if (!domain) {
+ pr_err("failed to add irq_domain\n");
+ return -EINVAL;
+ }
+
+ sde_kms->irq_controller.enabled_mask = 0;
+ sde_kms->irq_controller.domain = domain;
+
+ return 0;
+}
+
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms)
+{
+ if (sde_kms->irq_controller.domain) {
+ irq_domain_remove(sde_kms->irq_controller.domain);
+ sde_kms->irq_controller.domain = NULL;
+ }
+ return 0;
+}
+
irqreturn_t sde_core_irq(struct sde_kms *sde_kms)
{
/*
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.h b/drivers/gpu/drm/msm/sde/sde_core_irq.h
index 64f4160..c775f8c 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.h
@@ -38,6 +38,20 @@
void sde_core_irq_uninstall(struct sde_kms *sde_kms);
/**
+ * sde_core_irq_domain_add - Add core IRQ domain for SDE
+ * @sde_kms: SDE handle
+ * @return: none
+ */
+int sde_core_irq_domain_add(struct sde_kms *sde_kms);
+
+/**
+ * sde_core_irq_domain_fini - uninstall core IRQ domain
+ * @sde_kms: SDE handle
+ * @return: 0 if success; error code otherwise
+ */
+int sde_core_irq_domain_fini(struct sde_kms *sde_kms);
+
+/**
* sde_core_irq - core IRQ handler
* @sde_kms: SDE handle
* @return: interrupt handling status
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 7671649..9625d13 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -42,6 +42,23 @@
SDE_PERF_MODE_MAX
};
+/**
+ * enum sde_perf_vote_mode: perf vote mode.
+ * @APPS_RSC_MODE: It combines the vote for all displays and votes it
+ * through APPS rsc. This is default mode when display
+ * rsc is not available.
+ * @DISP_RSC_MODE: It combines the vote for all displays and votes it
+ * through display rsc. This is default configuration
+ * when display rsc is available.
+ * @DISP_RSC_PRIMARY_MODE: The primary display votes through display rsc
+ * while all other displays votes through apps rsc.
+ */
+enum sde_perf_vote_mode {
+ APPS_RSC_MODE,
+ DISP_RSC_MODE,
+ DISP_RSC_PRIMARY_MODE,
+};
+
static struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv;
@@ -182,12 +199,38 @@
return 0;
}
+static inline bool _is_crtc_client_type_matches(struct drm_crtc *tmp_crtc,
+ enum sde_crtc_client_type curr_client_type,
+ struct sde_core_perf *perf)
+{
+ if (!tmp_crtc)
+ return false;
+ else if (perf->bw_vote_mode == DISP_RSC_PRIMARY_MODE &&
+ perf->sde_rsc_available)
+ return curr_client_type == sde_crtc_get_client_type(tmp_crtc);
+ else
+ return true;
+}
+
+static inline enum sde_crtc_client_type _get_sde_client_type(
+ enum sde_crtc_client_type curr_client_type,
+ struct sde_core_perf *perf)
+{
+ if (perf->bw_vote_mode == DISP_RSC_PRIMARY_MODE &&
+ perf->sde_rsc_available)
+ return curr_client_type;
+ else if (perf->bw_vote_mode != APPS_RSC_MODE && perf->sde_rsc_available)
+ return RT_RSC_CLIENT;
+ else
+ return RT_CLIENT;
+}
+
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
struct drm_crtc *crtc)
{
u64 bw_sum_of_intfs = 0, bus_ab_quota, bus_ib_quota;
struct sde_core_perf_params perf = {0};
- enum sde_crtc_client_type curr_client_type
+ enum sde_crtc_client_type client_vote, curr_client_type
= sde_crtc_get_client_type(crtc);
struct drm_crtc *tmp_crtc;
struct sde_crtc_state *sde_cstate;
@@ -195,7 +238,8 @@
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
- (curr_client_type == sde_crtc_get_client_type(tmp_crtc))) {
+ _is_crtc_client_type_matches(tmp_crtc, curr_client_type,
+ &kms->perf)) {
sde_cstate = to_sde_crtc_state(tmp_crtc->state);
perf.max_per_pipe_ib = max(perf.max_per_pipe_ib,
@@ -217,7 +261,8 @@
bus_ib_quota = kms->perf.fix_core_ib_vote;
}
- switch (curr_client_type) {
+ client_vote = _get_sde_client_type(curr_client_type, &kms->perf);
+ switch (client_vote) {
case NRT_CLIENT:
sde_power_data_bus_set_quota(&priv->phandle, kms->core_client,
SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT,
@@ -246,6 +291,32 @@
SDE_ERROR("invalid client type:%d\n", curr_client_type);
break;
}
+
+ if (kms->perf.bw_vote_mode_updated) {
+ switch (kms->perf.bw_vote_mode) {
+ case DISP_RSC_MODE:
+ sde_power_data_bus_set_quota(&priv->phandle,
+ kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT, 0, 0);
+ sde_power_data_bus_set_quota(&priv->phandle,
+ kms->core_client,
+ SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, 0, 0);
+ kms->perf.bw_vote_mode_updated = false;
+ break;
+
+ case APPS_RSC_MODE:
+ sde_cstate = to_sde_crtc_state(crtc->state);
+ if (sde_cstate->rsc_client) {
+ sde_rsc_client_vote(sde_cstate->rsc_client,
+ 0, 0);
+ kms->perf.bw_vote_mode_updated = false;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
}
/**
@@ -535,6 +606,10 @@
(u32 *)&catalog->perf.max_bw_high);
debugfs_create_file("perf_mode", 0644, perf->debugfs_root,
(u32 *)perf, &sde_core_perf_mode_fops);
+ debugfs_create_u32("bw_vote_mode", 0600, perf->debugfs_root,
+ &perf->bw_vote_mode);
+ debugfs_create_bool("bw_vote_mode_updated", 0600, perf->debugfs_root,
+ &perf->bw_vote_mode_updated);
debugfs_create_u64("fix_core_clk_rate", 0644, perf->debugfs_root,
&perf->fix_core_clk_rate);
debugfs_create_u64("fix_core_ib_vote", 0644, perf->debugfs_root,
@@ -566,7 +641,6 @@
sde_core_perf_debugfs_destroy(perf);
perf->max_core_clk_rate = 0;
perf->core_clk = NULL;
- mutex_destroy(&perf->perf_lock);
perf->clk_name = NULL;
perf->phandle = NULL;
perf->catalog = NULL;
@@ -590,7 +664,12 @@
perf->phandle = phandle;
perf->pclient = pclient;
perf->clk_name = clk_name;
- mutex_init(&perf->perf_lock);
+ perf->sde_rsc_available = is_sde_rsc_available(SDE_RSC_INDEX);
+ /* set default mode */
+ if (perf->sde_rsc_available)
+ perf->bw_vote_mode = DISP_RSC_MODE;
+ else
+ perf->bw_vote_mode = APPS_RSC_MODE;
perf->core_clk = sde_power_clk_get_clk(phandle, clk_name);
if (!perf->core_clk) {
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index 31851be..68ee51a 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -53,7 +53,6 @@
* struct sde_core_perf - definition of core performance context
* @dev: Pointer to drm device
* @debugfs_root: top level debug folder
- * @perf_lock: serialization lock for this context
* @catalog: Pointer to catalog configuration
* @phandle: Pointer to power handler
* @pclient: Pointer to power client
@@ -66,11 +65,13 @@
* @fix_core_clk_rate: fixed core clock request in Hz used in mode 2
* @fix_core_ib_vote: fixed core ib vote in bps used in mode 2
* @fix_core_ab_vote: fixed core ab vote in bps used in mode 2
+ * @bw_vote_mode: apps rsc vs display rsc bandwidth vote mode
+ * @sde_rsc_available: is display rsc available
+ * @bw_vote_mode_updated: bandwidth vote mode update
*/
struct sde_core_perf {
struct drm_device *dev;
struct dentry *debugfs_root;
- struct mutex perf_lock;
struct sde_mdss_cfg *catalog;
struct sde_power_handle *phandle;
struct sde_power_client *pclient;
@@ -83,6 +84,9 @@
u64 fix_core_clk_rate;
u64 fix_core_ib_vote;
u64 fix_core_ab_vote;
+ u32 bw_vote_mode;
+ bool sde_rsc_available;
+ bool bw_vote_mode_updated;
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 79fcfb7..d0ade33 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2136,7 +2136,13 @@
dev = crtc->dev;
if (enable) {
- if (_sde_crtc_power_enable(sde_crtc, true))
+ int ret;
+
+ /* drop lock since power crtc cb may try to re-acquire lock */
+ mutex_unlock(&sde_crtc->crtc_lock);
+ ret = _sde_crtc_power_enable(sde_crtc, true);
+ mutex_lock(&sde_crtc->crtc_lock);
+ if (ret)
return;
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
@@ -2157,7 +2163,11 @@
sde_encoder_register_vblank_callback(enc, NULL, NULL);
}
+
+ /* drop lock since power crtc cb may try to re-acquire lock */
+ mutex_unlock(&sde_crtc->crtc_lock);
_sde_crtc_power_enable(sde_crtc, false);
+ mutex_lock(&sde_crtc->crtc_lock);
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index e3b658a..eeb7a00 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -49,90 +49,14 @@
return IRQ_HANDLED;
}
-static void sde_hw_irq_mask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- /* memory barrier */
- smp_mb__before_atomic();
- clear_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- /* memory barrier */
- smp_mb__after_atomic();
-}
-
-static void sde_hw_irq_unmask(struct irq_data *irqd)
-{
- struct sde_kms *sde_kms;
-
- if (!irqd || !irq_data_get_irq_chip_data(irqd)) {
- SDE_ERROR("invalid parameters irqd %d\n", irqd != 0);
- return;
- }
- sde_kms = irq_data_get_irq_chip_data(irqd);
-
- /* memory barrier */
- smp_mb__before_atomic();
- set_bit(irqd->hwirq, &sde_kms->irq_controller.enabled_mask);
- /* memory barrier */
- smp_mb__after_atomic();
-}
-
-static struct irq_chip sde_hw_irq_chip = {
- .name = "sde",
- .irq_mask = sde_hw_irq_mask,
- .irq_unmask = sde_hw_irq_unmask,
-};
-
-static int sde_hw_irqdomain_map(struct irq_domain *domain,
- unsigned int irq, irq_hw_number_t hwirq)
-{
- struct sde_kms *sde_kms;
- int rc;
-
- if (!domain || !domain->host_data) {
- SDE_ERROR("invalid parameters domain %d\n", domain != 0);
- return -EINVAL;
- }
- sde_kms = domain->host_data;
-
- irq_set_chip_and_handler(irq, &sde_hw_irq_chip, handle_level_irq);
- rc = irq_set_chip_data(irq, sde_kms);
-
- return rc;
-}
-
-static const struct irq_domain_ops sde_hw_irqdomain_ops = {
- .map = sde_hw_irqdomain_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
void sde_irq_preinstall(struct msm_kms *kms)
{
struct sde_kms *sde_kms = to_sde_kms(kms);
- struct device *dev;
- struct irq_domain *domain;
if (!sde_kms->dev || !sde_kms->dev->dev) {
pr_err("invalid device handles\n");
return;
}
- dev = sde_kms->dev->dev;
-
- domain = irq_domain_add_linear(dev->of_node, 32,
- &sde_hw_irqdomain_ops, sde_kms);
- if (!domain) {
- pr_err("failed to add irq_domain\n");
- return;
- }
-
- sde_kms->irq_controller.enabled_mask = 0;
- sde_kms->irq_controller.domain = domain;
sde_core_irq_preinstall(sde_kms);
}
@@ -162,9 +86,5 @@
}
sde_core_irq_uninstall(sde_kms);
-
- if (sde_kms->irq_controller.domain) {
- irq_domain_remove(sde_kms->irq_controller.domain);
- sde_kms->irq_controller.domain = NULL;
- }
+ sde_core_irq_domain_fini(sde_kms);
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 8cc196a..f0ce55c 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -744,6 +744,9 @@
priv = dev->dev_private;
catalog = sde_kms->catalog;
+ ret = sde_core_irq_domain_add(sde_kms);
+ if (ret)
+ goto fail_irq;
/*
* Query for underlying display drivers, and create connectors,
* bridges and encoders for them.
@@ -821,6 +824,8 @@
return 0;
fail:
_sde_kms_drm_obj_destroy(sde_kms);
+fail_irq:
+ sde_core_irq_domain_fini(sde_kms);
return ret;
}
@@ -1539,6 +1544,14 @@
goto perf_err;
}
+ sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
+ if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
+ rc = PTR_ERR(sde_kms->hw_intr);
+ SDE_ERROR("hw_intr init failed: %d\n", rc);
+ sde_kms->hw_intr = NULL;
+ goto hw_intr_init_err;
+ }
+
/*
* _sde_kms_drm_obj_init should create the DRM related objects
* i.e. CRTCs, planes, encoders, connectors and so forth
@@ -1564,23 +1577,12 @@
*/
dev->mode_config.allow_fb_modifiers = true;
- sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
- if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
- rc = PTR_ERR(sde_kms->hw_intr);
- if (!sde_kms->hw_intr)
- rc = -EINVAL;
- SDE_ERROR("hw_intr init failed: %d\n", rc);
- sde_kms->hw_intr = NULL;
- goto hw_intr_init_err;
- }
-
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
return 0;
-hw_intr_init_err:
- _sde_kms_drm_obj_destroy(sde_kms);
drm_obj_init_err:
sde_core_perf_destroy(&sde_kms->perf);
+hw_intr_init_err:
perf_err:
power_error:
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
new file mode 100644
index 0000000..12165e8
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -0,0 +1,511 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm_edid.h>
+
+#include "sde_kms.h"
+#include "sde_edid_parser.h"
+
+#define DBC_START_OFFSET 4
+#define EDID_DTD_LEN 18
+
+enum data_block_types {
+ RESERVED,
+ AUDIO_DATA_BLOCK,
+ VIDEO_DATA_BLOCK,
+ VENDOR_SPECIFIC_DATA_BLOCK,
+ SPEAKER_ALLOCATION_DATA_BLOCK,
+ VESA_DTC_DATA_BLOCK,
+ RESERVED2,
+ USE_EXTENDED_TAG
+};
+
+static u8 *sde_find_edid_extension(struct edid *edid, int ext_id)
+{
+ u8 *edid_ext = NULL;
+ int i;
+
+ /* No EDID or EDID extensions */
+ if (edid == NULL || edid->extensions == 0)
+ return NULL;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == ext_id)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return NULL;
+
+ return edid_ext;
+}
+
+static u8 *sde_find_cea_extension(struct edid *edid)
+{
+ return sde_find_edid_extension(edid, SDE_CEA_EXT);
+}
+
+static int
+sde_cea_db_payload_len(const u8 *db)
+{
+ return db[0] & 0x1f;
+}
+
+static int
+sde_cea_db_tag(const u8 *db)
+{
+ return db[0] >> 5;
+}
+
+static int
+sde_cea_revision(const u8 *cea)
+{
+ return cea[1];
+}
+
+static int
+sde_cea_db_offsets(const u8 *cea, int *start, int *end)
+{
+ /* Data block offset in CEA extension block */
+ *start = 4;
+ *end = cea[2];
+ if (*end == 0)
+ *end = 127;
+ if (*end < 4 || *end > 127)
+ return -ERANGE;
+ return 0;
+}
+
+#define sde_for_each_cea_db(cea, i, start, end) \
+for ((i) = (start); \
+(i) < (end) && (i) + sde_cea_db_payload_len(&(cea)[(i)]) < (end); \
+(i) += sde_cea_db_payload_len(&(cea)[(i)]) + 1)
+
+static u8 *sde_edid_find_extended_tag_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if ((sde_cea_db_tag(db) == SDE_EXTENDED_TAG) &&
+ (db[1] == blk_id))
+ return db;
+ }
+ }
+ return NULL;
+}
+
+static u8 *
+sde_edid_find_block(struct edid *edid, int blk_id)
+{
+ u8 *db = NULL;
+ u8 *cea = NULL;
+
+ if (!edid) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return NULL;
+ }
+
+ cea = sde_find_cea_extension(edid);
+
+ if (cea && sde_cea_revision(cea) >= 3) {
+ int i, start, end;
+
+ if (sde_cea_db_offsets(cea, &start, &end))
+ return NULL;
+
+ sde_for_each_cea_db(cea, i, start, end) {
+ db = &cea[i];
+ if (sde_cea_db_tag(db) == blk_id)
+ return db;
+ }
+ }
+ return NULL;
+}
+
+
+static const u8 *_sde_edid_find_block(const u8 *in_buf, u32 start_offset,
+ u8 type, u8 *len)
+{
+ /* the start of data block collection, start of Video Data Block */
+ u32 offset = start_offset;
+ u32 dbc_offset = in_buf[2];
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ /*
+ * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block
+ * collection present.
+ * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block
+ * collection present and no DTD data present.
+ */
+ if ((dbc_offset == 0) || (dbc_offset == 4)) {
+ SDE_ERROR("EDID: no DTD or non-DTD data present\n");
+ return NULL;
+ }
+
+ while (offset < dbc_offset) {
+ u8 block_len = in_buf[offset] & 0x1F;
+
+ if ((offset + block_len <= dbc_offset) &&
+ (in_buf[offset] >> 5) == type) {
+ *len = block_len;
+ SDE_EDID_DEBUG("block=%d found @ 0x%x w/ len=%d\n",
+ type, offset, block_len);
+
+ return in_buf + offset;
+ }
+ offset += 1 + block_len;
+ }
+
+ return NULL;
+}
+
+static void sde_edid_extract_vendor_id(struct sde_edid_ctrl *edid_ctrl)
+{
+ char *vendor_id;
+ u32 id_codes;
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ vendor_id = edid_ctrl->vendor_id;
+ id_codes = ((u32)edid_ctrl->edid->mfg_id[0] << 8) +
+ edid_ctrl->edid->mfg_id[1];
+
+ vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F);
+ vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F);
+ vendor_id[2] = 'A' - 1 + (id_codes & 0x1F);
+ vendor_id[3] = 0;
+ SDE_EDID_DEBUG("vendor id is %s ", vendor_id);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_y420_support(struct drm_connector *connector,
+u32 video_format)
+{
+ u8 cea_mode = 0;
+ struct drm_display_mode *mode;
+
+ /* Need to add Y420 support flag to the modes */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ cea_mode = drm_match_cea_mode(mode);
+ if ((cea_mode != 0) && (cea_mode == video_format)) {
+ SDE_EDID_DEBUG("%s found match for %d ", __func__,
+ video_format);
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_YUV;
+ }
+ }
+}
+
+static void sde_edid_parse_Y420CMDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u32 offset = 0;
+ u8 len = 0;
+ u8 svd_len = 0;
+ const u8 *svd = NULL;
+ u32 i = 0, j = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: edid_ctrl is NULL\n", __func__);
+ return;
+ }
+
+ if (!db) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ len = db[0] & 0x1f;
+
+ if (len < 7)
+ return;
+ /* Byte 3 to L+1 contain SVDs */
+ offset += 2;
+
+ svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK);
+
+ if (svd) {
+ /*moving to the next byte as vic info begins there*/
+ ++svd;
+ svd_len = svd[0] & 0x1f;
+ }
+
+ for (i = 0; i < svd_len; i++, j++) {
+ video_format = *svd & 0x7F;
+ if (db[offset] & (1 << j))
+ sde_edid_set_y420_support(connector, video_format);
+
+ if (j & 0x80) {
+ j = j/8;
+ offset++;
+ if (offset >= len)
+ break;
+ }
+ }
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+
+}
+
+static void sde_edid_parse_Y420VDB(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl,
+const u8 *db)
+{
+ u8 len = db[0] & 0x1f;
+ u32 i = 0;
+ u32 video_format = 0;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ /* Offset to byte 3 */
+ db += 2;
+ for (i = 0; i < len - 1; i++) {
+ video_format = *(db + i) & 0x7F;
+ /*
+ * mode was already added in get_modes()
+ * only need to set the Y420 support flag
+ */
+ sde_edid_set_y420_support(connector, video_format);
+ }
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void sde_edid_set_mode_format(
+struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl)
+{
+ const u8 *db = NULL;
+ struct drm_display_mode *mode;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ /* Set YUV mode support flags for YCbcr420VDB */
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_VIDEO_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420VDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 VDB is not present\n");
+
+ /* Set RGB supported on all modes where YUV is not set */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ if (!(mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV))
+ mode->flags |= DRM_MODE_FLAG_SUPPORTS_RGB;
+ }
+
+
+ db = sde_edid_find_extended_tag_block(edid_ctrl->edid,
+ Y420_CAPABILITY_MAP_DATA_BLOCK);
+ if (db)
+ sde_edid_parse_Y420CMDB(connector, edid_ctrl, db);
+ else
+ SDE_EDID_DEBUG("YCbCr420 CMDB is not present\n");
+
+ SDE_EDID_DEBUG("%s -\n", __func__);
+}
+
+static void _sde_edid_extract_audio_data_blocks(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len = 0;
+ u8 adb_max = 0;
+ const u8 *adb = NULL;
+ u32 offset = DBC_START_OFFSET;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ edid_ctrl->adb_size = 0;
+
+ memset(edid_ctrl->audio_data_block, 0,
+ sizeof(edid_ctrl->audio_data_block));
+
+ do {
+ len = 0;
+ adb = _sde_edid_find_block(cea, offset, AUDIO_DATA_BLOCK,
+ &len);
+
+ if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE ||
+ adb_max >= MAX_NUMBER_ADB)) {
+ if (!edid_ctrl->adb_size) {
+ SDE_DEBUG("No/Invalid Audio Data Block\n");
+ return;
+ }
+
+ continue;
+ }
+
+ memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size,
+ adb + 1, len);
+ offset = (adb - cea) + 1 + len;
+
+ edid_ctrl->adb_size += len;
+ adb_max++;
+ } while (adb);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+static void _sde_edid_extract_speaker_allocation_data(
+ struct sde_edid_ctrl *edid_ctrl)
+{
+ u8 len;
+ const u8 *sadb = NULL;
+ u8 *cea = NULL;
+
+ if (!edid_ctrl) {
+ SDE_ERROR("invalid edid_ctrl\n");
+ return;
+ }
+ SDE_EDID_DEBUG("%s +", __func__);
+ cea = sde_find_cea_extension(edid_ctrl->edid);
+ if (!cea) {
+ SDE_DEBUG("CEA extension not found\n");
+ return;
+ }
+
+ sadb = _sde_edid_find_block(cea, DBC_START_OFFSET,
+ SPEAKER_ALLOCATION_DATA_BLOCK, &len);
+ if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) {
+ SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n");
+ return;
+ }
+
+ memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len);
+ edid_ctrl->sadb_size = len;
+
+ SDE_EDID_DEBUG("speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n",
+ sadb[1],
+ (sadb[1] & BIT(0)) ? "FL/FR," : "",
+ (sadb[1] & BIT(1)) ? "LFE," : "",
+ (sadb[1] & BIT(2)) ? "FC," : "",
+ (sadb[1] & BIT(3)) ? "RL/RR," : "",
+ (sadb[1] & BIT(4)) ? "RC," : "",
+ (sadb[1] & BIT(5)) ? "FLC/FRC," : "",
+ (sadb[1] & BIT(6)) ? "RLC/RRC," : "");
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+struct sde_edid_ctrl *sde_edid_init(void)
+{
+ struct sde_edid_ctrl *edid_ctrl = NULL;
+
+ SDE_EDID_DEBUG("%s +\n", __func__);
+ edid_ctrl = kzalloc(sizeof(*edid_ctrl), GFP_KERNEL);
+ if (!edid_ctrl) {
+ SDE_ERROR("edid_ctrl alloc failed\n");
+ return NULL;
+ }
+ memset((edid_ctrl), 0, sizeof(*edid_ctrl));
+ SDE_EDID_DEBUG("%s -\n", __func__);
+ return edid_ctrl;
+}
+
+void sde_free_edid(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ kfree(edid_ctrl->edid);
+ edid_ctrl->edid = NULL;
+}
+
+void sde_edid_deinit(void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ sde_free_edid((void *)&edid_ctrl);
+ kfree(edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+}
+
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *input)
+{
+ int rc = 0;
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ SDE_EDID_DEBUG("%s +", __func__);
+ if (edid_ctrl->edid) {
+ drm_mode_connector_update_edid_property(connector,
+ edid_ctrl->edid);
+
+ rc = drm_add_edid_modes(connector, edid_ctrl->edid);
+ sde_edid_set_mode_format(connector, edid_ctrl);
+ SDE_EDID_DEBUG("%s -", __func__);
+ return rc;
+ }
+
+ drm_mode_connector_update_edid_property(connector, NULL);
+ SDE_EDID_DEBUG("%s null edid -", __func__);
+ return rc;
+}
+
+bool sde_detect_hdmi_monitor(void *input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
+
+ return drm_detect_hdmi_monitor(edid_ctrl->edid);
+}
+
+void sde_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter, void **input)
+{
+ struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input);
+
+ edid_ctrl->edid = drm_get_edid(connector, adapter);
+ SDE_EDID_DEBUG("%s +\n", __func__);
+
+ if (!edid_ctrl->edid)
+ SDE_ERROR("EDID read failed\n");
+
+ if (edid_ctrl->edid) {
+ sde_edid_extract_vendor_id(edid_ctrl);
+ _sde_edid_extract_audio_data_blocks(edid_ctrl);
+ _sde_edid_extract_speaker_allocation_data(edid_ctrl);
+ }
+ SDE_EDID_DEBUG("%s -\n", __func__);
+};
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h
new file mode 100644
index 0000000..1143dc2
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_edid_parser.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SDE_EDID_PARSER_H_
+#define _SDE_EDID_PARSER_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+
+#define MAX_NUMBER_ADB 5
+#define MAX_AUDIO_DATA_BLOCK_SIZE 30
+#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+#define EDID_VENDOR_ID_SIZE 4
+
+#define SDE_CEA_EXT 0x02
+#define SDE_EXTENDED_TAG 0x07
+
+enum extended_data_block_types {
+ VIDEO_CAPABILITY_DATA_BLOCK = 0x0,
+ VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01,
+ HDMI_VIDEO_DATA_BLOCK = 0x04,
+ HDR_STATIC_METADATA_DATA_BLOCK = 0x06,
+ Y420_VIDEO_DATA_BLOCK = 0x0E,
+ VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D,
+ Y420_CAPABILITY_MAP_DATA_BLOCK = 0x0F,
+ VENDOR_SPECIFIC_AUDIO_DATA_BLOCK = 0x11,
+ INFOFRAME_DATA_BLOCK = 0x20,
+};
+
+#ifdef SDE_EDID_DEBUG_ENABLE
+#define SDE_EDID_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
+#else
+#define SDE_EDID_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args)
+#endif
+
+/*
+ * struct hdmi_edid_hdr_data - HDR Static Metadata
+ * @eotf: Electro-Optical Transfer Function
+ * @metadata_type_one: Static Metadata Type 1 support
+ * @max_luminance: Desired Content Maximum Luminance
+ * @avg_luminance: Desired Content Frame-average Luminance
+ * @min_luminance: Desired Content Minimum Luminance
+ */
+struct sde_edid_hdr_data {
+ u32 eotf;
+ bool metadata_type_one;
+ u32 max_luminance;
+ u32 avg_luminance;
+ u32 min_luminance;
+};
+
+struct sde_edid_sink_caps {
+ u32 max_pclk_in_hz;
+ bool scdc_present;
+ bool scramble_support; /* scramble support for less than 340Mcsc */
+ bool read_req_support;
+ bool osd_disparity;
+ bool dual_view_support;
+ bool ind_view_support;
+};
+
+struct sde_edid_ctrl {
+ struct edid *edid;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
+ int adb_size;
+ u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
+ int sadb_size;
+ bool hdr_supported;
+ char vendor_id[EDID_VENDOR_ID_SIZE];
+ struct sde_edid_sink_caps sink_caps;
+ struct sde_edid_hdr_data hdr_data;
+};
+
+/**
+ * sde_edid_init() - init edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ * Return: handle to sde_edid_ctrl for the client.
+ */
+struct sde_edid_ctrl *sde_edid_init(void);
+
+/**
+ * sde_edid_deinit() - deinit edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_edid_deinit(void **edid_ctrl);
+
+/**
+ * sde_get_edid() - get edid info.
+ * @connector: Handle to the drm_connector.
+ * @adapter: handle to i2c adapter for DDC read
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_get_edid(struct drm_connector *connector,
+struct i2c_adapter *adapter,
+void **edid_ctrl);
+
+/**
+ * sde_free_edid() - free edid structure.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: void.
+ */
+void sde_free_edid(void **edid_ctrl);
+
+/**
+ * sde_detect_hdmi_monitor() - detect HDMI mode.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+bool sde_detect_hdmi_monitor(void *edid_ctrl);
+
+/**
+ * _sde_edid_update_modes() - populate EDID modes.
+ * @edid_ctrl: Handle to the edid_ctrl structure.
+ *
+ * Return: error code.
+ */
+int _sde_edid_update_modes(struct drm_connector *connector,
+ void *edid_ctrl);
+
+#endif /* _SDE_EDID_PARSER_H_ */
+
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index cab7e0f..7bf2211 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -123,6 +123,7 @@
void sde_rsc_client_destroy(struct sde_rsc_client *client)
{
struct sde_rsc_priv *rsc;
+ enum sde_rsc_state state;
if (!client) {
pr_debug("invalid client\n");
@@ -138,9 +139,13 @@
goto end;
mutex_lock(&rsc->client_lock);
- if (client->current_state != SDE_RSC_IDLE_STATE)
+ state = client->current_state;
+ mutex_unlock(&rsc->client_lock);
+
+ if (state != SDE_RSC_IDLE_STATE)
sde_rsc_client_state_update(client, SDE_RSC_IDLE_STATE,
NULL, -1);
+ mutex_lock(&rsc->client_lock);
list_del_init(&client->list);
mutex_unlock(&rsc->client_lock);
@@ -215,6 +220,39 @@
}
EXPORT_SYMBOL(sde_rsc_unregister_event);
+bool is_sde_rsc_available(int rsc_index)
+{
+ if (rsc_index >= MAX_RSC_COUNT) {
+ pr_err("invalid rsc index:%d\n", rsc_index);
+ return false;
+ } else if (!rsc_prv_list[rsc_index]) {
+ pr_err("rsc idx:%d not probed yet or not available\n",
+ rsc_index);
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(is_sde_rsc_available);
+
+enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
+{
+ struct sde_rsc_priv *rsc;
+
+ if (rsc_index >= MAX_RSC_COUNT) {
+ pr_err("invalid rsc index:%d\n", rsc_index);
+ return SDE_RSC_IDLE_STATE;
+ } else if (!rsc_prv_list[rsc_index]) {
+ pr_err("rsc idx:%d not probed yet or not available\n",
+ rsc_index);
+ return SDE_RSC_IDLE_STATE;
+ }
+
+ rsc = rsc_prv_list[rsc_index];
+ return rsc->current_state;
+}
+EXPORT_SYMBOL(get_sde_rsc_current_state);
+
static int sde_rsc_clk_enable(struct sde_power_handle *phandle,
struct sde_power_client *pclient, bool enable)
{
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index e982afe..1f76233a 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -860,6 +860,9 @@
#define A6XX_GMU_AHB_FENCE_RANGE_0 0x23B11
#define A6XX_GMU_AHB_FENCE_RANGE_1 0x23B12
+/* GPUCC registers */
+#define A6XX_GPU_CC_GX_GDSCR 0x24403
+
/* GPU RSC sequencer registers */
#define A6XX_RSCC_PDC_SEQ_START_ADDR 0x23408
#define A6XX_RSCC_PDC_MATCH_VALUE_LO 0x23409
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 6e025c8..9a56bec 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -1007,6 +1007,7 @@
#define SPTPRAC_POWEROFF_STATUS_MASK BIT(2)
#define SPTPRAC_POWERON_STATUS_MASK BIT(3)
#define SPTPRAC_CTRL_TIMEOUT 10 /* ms */
+#define A6XX_RETAIN_FF_ENABLE_ENABLE_MASK BIT(11)
/*
* a6xx_sptprac_enable() - Power on SPTPRAC
@@ -1047,6 +1048,10 @@
if (!gmu->pdev)
return;
+ /* Ensure that retention is on */
+ kgsl_gmu_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0,
+ A6XX_RETAIN_FF_ENABLE_ENABLE_MASK);
+
kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL,
SPTPRAC_POWEROFF_CTRL_MASK);
@@ -1101,6 +1106,10 @@
if (!regulator_is_enabled(gmu->gx_gdsc))
return 0;
+ /* Ensure that retention is on */
+ kgsl_gmu_regrmw(device, A6XX_GPU_CC_GX_GDSCR, 0,
+ A6XX_RETAIN_FF_ENABLE_ENABLE_MASK);
+
clk_disable_unprepare(pwr->grp_clks[0]);
clk_set_rate(pwr->grp_clks[0],
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 7354e82..f72b3fa 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1543,8 +1543,6 @@
if (gmu->reg_virt) {
devm_iounmap(&gmu->pdev->dev, gmu->reg_virt);
- devm_release_mem_region(&gmu->pdev->dev,
- gmu->reg_phys, gmu->reg_len);
gmu->reg_virt = NULL;
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 49085d7..4b2db07 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -3017,7 +3017,7 @@
/* Create Worker for ife_hw_mgr with 10 tasks */
rc = cam_req_mgr_workq_create("cam_ife_worker", 10,
- &g_ife_hw_mgr.workq);
+ &g_ife_hw_mgr.workq, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
pr_err("%s: Unable to create worker\n", __func__);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index e62c101..ed251eb 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -1092,10 +1092,9 @@
slot = &in_q->slot[in_q->wr_idx];
if (slot->status != CRM_SLOT_STATUS_NO_REQ &&
- slot->status != CRM_SLOT_STATUS_REQ_APPLIED) {
- CRM_ERR("in_q overwrite %d", slot->status);
- /* @TODO: error handling */
- }
+ slot->status != CRM_SLOT_STATUS_REQ_APPLIED)
+ CRM_WARN("in_q overwrite %d", slot->status);
+
CRM_DBG("sched_req %lld at slot %d",
sched_req->req_id, in_q->wr_idx);
@@ -1106,7 +1105,6 @@
__cam_req_mgr_inc_idx(&in_q->wr_idx, 1, in_q->num_slots);
mutex_unlock(&link->req.lock);
- complete(&link->workq_comp);
end:
return rc;
}
@@ -1371,6 +1369,7 @@
goto end;
}
+ CRM_DBG("E: dev %x dev req %lld", add_req->dev_hdl, add_req->req_id);
link = (struct cam_req_mgr_core_link *)
cam_get_device_priv(add_req->link_hdl);
@@ -1404,6 +1403,7 @@
dev_req->dev_hdl = add_req->dev_hdl;
task->process_cb = &cam_req_mgr_process_add_req;
rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
+ CRM_DBG("X: dev %x dev req %lld", add_req->dev_hdl, add_req->req_id);
end:
return rc;
@@ -1813,7 +1813,8 @@
/* Create worker for current link */
snprintf(buf, sizeof(buf), "%x-%x",
link_info->session_hdl, link->link_hdl);
- rc = cam_req_mgr_workq_create(buf, CRM_WORKQ_NUM_TASKS, &link->workq);
+ rc = cam_req_mgr_workq_create(buf, CRM_WORKQ_NUM_TASKS,
+ &link->workq, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
CRM_ERR("FATAL: unable to create worker");
__cam_req_mgr_destroy_link_info(link);
@@ -1919,11 +1920,10 @@
struct cam_req_mgr_sched_request *sched_req)
{
int rc = 0;
- struct crm_workq_task *task = NULL;
struct cam_req_mgr_core_link *link = NULL;
struct cam_req_mgr_core_session *session = NULL;
struct cam_req_mgr_sched_request *sched;
- struct crm_task_payload *task_data;
+ struct crm_task_payload task_data;
if (!sched_req) {
CRM_ERR("csl_req is NULL");
@@ -1942,14 +1942,10 @@
CRM_WARN("session ptr NULL %x", sched_req->link_hdl);
return -EINVAL;
}
+ CRM_DBG("link %x req %lld", sched_req->link_hdl, sched_req->req_id);
- task = cam_req_mgr_workq_get_task(link->workq);
- if (!task)
- return -ENOMEM;
-
- task_data = (struct crm_task_payload *)task->payload;
- task_data->type = CRM_WORKQ_TASK_SCHED_REQ;
- sched = (struct cam_req_mgr_sched_request *)&task_data->u;
+ task_data.type = CRM_WORKQ_TASK_SCHED_REQ;
+ sched = (struct cam_req_mgr_sched_request *)&task_data.u;
sched->req_id = sched_req->req_id;
sched->link_hdl = sched_req->link_hdl;
if (session->force_err_recovery == AUTO_RECOVERY) {
@@ -1958,14 +1954,10 @@
sched->bubble_enable =
(session->force_err_recovery == FORCE_ENABLE_RECOVERY) ? 1 : 0;
}
- task->process_cb = &cam_req_mgr_process_sched_req;
- rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
- /* Blocking call */
- init_completion(&link->workq_comp);
- rc = wait_for_completion_timeout(
- &link->workq_comp,
- msecs_to_jiffies(CAM_REQ_MGR_SCHED_REQ_TIMEOUT));
+ rc = cam_req_mgr_process_sched_req(link, &task_data);
+
+ CRM_DBG("DONE dev %x req %lld", sched_req->link_hdl, sched_req->req_id);
end:
return rc;
}
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index 889ee9c..3ee0e2f 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -27,7 +27,7 @@
#define FORCE_ENABLE_RECOVERY 1
#define AUTO_RECOVERY 0
-#define CRM_WORKQ_NUM_TASKS 30
+#define CRM_WORKQ_NUM_TASKS 60
/**
* enum crm_workq_task_type
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
index f53e41c..38dcb42 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.c
@@ -12,16 +12,30 @@
#include "cam_req_mgr_workq.h"
+#define WORKQ_ACQUIRE_LOCK(workq, flags) {\
+ if ((workq)->in_irq) \
+ spin_lock_irqsave(&(workq)->lock_bh, (flags)); \
+ else \
+ spin_lock_bh(&(workq)->lock_bh); \
+}
+
+#define WORKQ_RELEASE_LOCK(workq, flags) {\
+ if ((workq)->in_irq) \
+ spin_unlock_irqrestore(&(workq)->lock_bh, (flags)); \
+ else \
+ spin_unlock_bh(&(workq)->lock_bh); \
+}
struct crm_workq_task *cam_req_mgr_workq_get_task(
struct cam_req_mgr_core_workq *workq)
{
struct crm_workq_task *task = NULL;
+ unsigned long flags = 0;
if (!workq)
return NULL;
- spin_lock_bh(&workq->lock_bh);
+ WORKQ_ACQUIRE_LOCK(workq, flags);
if (list_empty(&workq->task.empty_head))
goto end;
@@ -33,7 +47,8 @@
}
end:
- spin_unlock_bh(&workq->lock_bh);
+ WORKQ_RELEASE_LOCK(workq, flags);
+
return task;
}
@@ -41,8 +56,9 @@
{
struct cam_req_mgr_core_workq *workq =
(struct cam_req_mgr_core_workq *)task->parent;
+ unsigned long flags = 0;
- spin_lock_bh(&workq->lock_bh);
+ WORKQ_ACQUIRE_LOCK(workq, flags);
list_del_init(&task->entry);
task->cancel = 0;
task->process_cb = NULL;
@@ -50,7 +66,7 @@
list_add_tail(&task->entry,
&workq->task.empty_head);
atomic_add(1, &workq->task.free_cnt);
- spin_unlock_bh(&workq->lock_bh);
+ WORKQ_RELEASE_LOCK(workq, flags);
}
/**
@@ -131,6 +147,7 @@
{
int rc = 0;
struct cam_req_mgr_core_workq *workq = NULL;
+ unsigned long flags = 0;
if (!task) {
CRM_WARN("NULL task pointer can not schedule");
@@ -148,24 +165,25 @@
goto end;
}
- spin_lock_bh(&workq->lock_bh);
if (task->cancel == 1) {
cam_req_mgr_workq_put_task(task);
CRM_WARN("task aborted and queued back to pool");
rc = 0;
- spin_unlock_bh(&workq->lock_bh);
goto end;
}
task->priv = priv;
task->priority =
(prio < CRM_TASK_PRIORITY_MAX && prio >= CRM_TASK_PRIORITY_0)
? prio : CRM_TASK_PRIORITY_0;
+
+ WORKQ_ACQUIRE_LOCK(workq, flags);
list_add_tail(&task->entry,
&workq->task.process_head[task->priority]);
+ WORKQ_RELEASE_LOCK(workq, flags);
+
atomic_add(1, &workq->task.pending_cnt);
CRM_DBG("enq task %pK pending_cnt %d",
task, atomic_read(&workq->task.pending_cnt));
- spin_unlock_bh(&workq->lock_bh);
queue_work(workq->job, &workq->work);
@@ -174,7 +192,7 @@
}
int cam_req_mgr_workq_create(char *name, int32_t num_tasks,
- struct cam_req_mgr_core_workq **workq)
+ struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq)
{
int32_t i;
struct crm_workq_task *task;
@@ -209,6 +227,7 @@
for (i = CRM_TASK_PRIORITY_0; i < CRM_TASK_PRIORITY_MAX; i++)
INIT_LIST_HEAD(&crm_workq->task.process_head[i]);
INIT_LIST_HEAD(&crm_workq->task.empty_head);
+ crm_workq->in_irq = in_irq;
crm_workq->task.num_task = num_tasks;
crm_workq->task.pool = (struct crm_workq_task *)
kzalloc(sizeof(struct crm_workq_task) *
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
index 7d8ca59..eb3b804 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_workq.h
@@ -25,9 +25,16 @@
/* Task priorities, lower the number higher the priority*/
enum crm_task_priority {
- CRM_TASK_PRIORITY_0 = 0,
- CRM_TASK_PRIORITY_1 = 1,
- CRM_TASK_PRIORITY_MAX = 2,
+ CRM_TASK_PRIORITY_0,
+ CRM_TASK_PRIORITY_1,
+ CRM_TASK_PRIORITY_MAX,
+};
+
+/* workqueue will be used from irq context or not */
+enum crm_workq_context {
+ CRM_WORKQ_USAGE_NON_IRQ,
+ CRM_WORKQ_USAGE_IRQ,
+ CRM_WORKQ_USAGE_INVALID,
};
/** struct crm_workq_task
@@ -58,8 +65,9 @@
* @work : work token used by workqueue
* @job : workqueue internal job struct
* task -
- * @lock : lock for task structs
- * @free_cnt : num of free/available tasks
+ * @lock_bh : lock for task structs
+ * @in_irq : set true if workque can be used in irq context
+ * @free_cnt : num of free/available tasks
* @empty_head : list head of available taska which can be used
* or acquired in order to enqueue a task to workq
* @pool : pool of tasks used for handling events in workq context
@@ -70,6 +78,7 @@
struct work_struct work;
struct workqueue_struct *job;
spinlock_t lock_bh;
+ uint32_t in_irq;
/* tasks */
struct {
@@ -91,11 +100,12 @@
* of session handle and link handle
* @num_task : Num_tasks to be allocated for workq
* @workq : Double pointer worker
+ * @in_irq : Set to one if workq might be used in irq context
* This function will allocate and create workqueue and pass
* the workq pointer to caller.
*/
int cam_req_mgr_workq_create(char *name, int32_t num_tasks,
- struct cam_req_mgr_core_workq **workq);
+ struct cam_req_mgr_core_workq **workq, enum crm_workq_context in_irq);
/**
* cam_req_mgr_workq_destroy()
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 2fa39c8..140542b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1904,14 +1904,14 @@
}
rc = cam_req_mgr_workq_create("icp_command_queue", ICP_WORKQ_NUM_TASK,
- &icp_hw_mgr.cmd_work);
+ &icp_hw_mgr.cmd_work, CRM_WORKQ_USAGE_NON_IRQ);
if (rc < 0) {
pr_err("unable to create a worker\n");
goto cmd_work_failed;
}
rc = cam_req_mgr_workq_create("icp_message_queue", ICP_WORKQ_NUM_TASK,
- &icp_hw_mgr.msg_work);
+ &icp_hw_mgr.msg_work, CRM_WORKQ_USAGE_IRQ);
if (rc < 0) {
pr_err("unable to create a worker\n");
goto msg_work_failed;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index b3dc213..44a29aa 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -302,13 +302,13 @@
return 0;
}
-static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
+static int sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
{
int ret;
- if (WARN_ON(mgr->regulator_enable == on)) {
+ if (mgr->regulator_enable == on) {
SDEROT_ERR("Regulators already in selected mode on=%d\n", on);
- return;
+ return 0;
}
SDEROT_EVTLOG(on);
@@ -330,9 +330,9 @@
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
- SDEROT_WARN("Rotator regulator failed to %s\n",
- on ? "enable" : "disable");
- return;
+ pr_err("rotator regulator failed to %s ret:%d client:%d\n",
+ on ? "enable" : "disable", ret, mgr->rsc_client != NULL);
+ return ret;
}
if (mgr->ops_hw_post_pmevent)
@@ -344,6 +344,7 @@
}
mgr->regulator_enable = on;
+ return 0;
}
static int sde_rotator_enable_clk(struct sde_rot_mgr *mgr, int clk_idx)
@@ -2922,12 +2923,11 @@
}
*pmgr = mgr;
-
- pm_runtime_set_suspended(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- SDEROT_ERR("fail to enable power, force on\n");
- sde_rotator_footswitch_ctrl(mgr, true);
+ ret = sde_rotator_footswitch_ctrl(mgr, true);
+ if (ret) {
+ SDEROT_ERR("res_init failed %d\n", ret);
+ ret = -EPROBE_DEFER;
+ goto error_fs_en_fail;
}
/* enable power and clock before h/w initialization/query */
@@ -2968,6 +2968,9 @@
/* disable power and clock after h/w initialization/query */
sde_rotator_clk_ctrl(mgr, false);
sde_rotator_resource_ctrl(mgr, false);
+ sde_rotator_footswitch_ctrl(mgr, false);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
return 0;
@@ -2977,7 +2980,8 @@
error_map_hw_ops:
sde_rotator_clk_ctrl(mgr, false);
sde_rotator_resource_ctrl(mgr, false);
- pm_runtime_disable(mgr->device);
+ sde_rotator_footswitch_ctrl(mgr, false);
+error_fs_en_fail:
sde_rotator_res_destroy(mgr);
error_res_init:
error_parse_dt:
@@ -3061,8 +3065,7 @@
SDEROT_DBG("begin runtime_active\n");
ATRACE_BEGIN("runtime_active");
- sde_rotator_footswitch_ctrl(mgr, true);
- return 0;
+ return sde_rotator_footswitch_ctrl(mgr, true);
}
/*
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 14eb3ab..aa5f18d 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1202,6 +1202,16 @@
else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES)
num_b = ctrl->val;
+ if ((num_b < inst->capability.bframe.min) ||
+ (num_b > inst->capability.bframe.max)) {
+ dprintk(VIDC_ERR,
+ "Error setting num b frames %d min, max supported is %d, %d\n",
+ num_b, inst->capability.bframe.min,
+ inst->capability.bframe.max);
+ rc = -ENOTSUPP;
+ break;
+ }
+
property_id = HAL_CONFIG_VENC_INTRA_PERIOD;
intra_period.pframes = num_p;
intra_period.bframes = num_b;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 04876d2..499d851 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -168,6 +168,9 @@
case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT:
msm_vidc_ctrl_get_range(ctrl, &inst->capability.blur_height);
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES:
+ msm_vidc_ctrl_get_range(ctrl, &inst->capability.bframe);
+ break;
default:
rc = -EINVAL;
}
@@ -1932,6 +1935,7 @@
ctrl->val = bufreq->buffer_count_min_host;
break;
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ msm_comm_try_get_bufreqs(inst);
bufreq = get_buff_req_buffer(inst, HAL_BUFFER_INPUT);
if (!bufreq) {
dprintk(VIDC_ERR,
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index ad627fb..7ad650e 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2980,7 +2980,8 @@
goto out2;
if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ !(rdev->desc->ops->get_voltage ||
+ rdev->desc->ops->get_voltage_sel))) {
int current_supply_uV;
int selector;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 69e0ebc..2603b6d 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -4002,12 +4002,13 @@
.llseek = seq_lseek,
};
+#ifdef CONFIG_ICNSS_DEBUG
static int icnss_debugfs_create(struct icnss_priv *priv)
{
int ret = 0;
struct dentry *root_dentry;
- root_dentry = debugfs_create_dir("icnss", 0);
+ root_dentry = debugfs_create_dir("icnss", NULL);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
@@ -4017,19 +4018,40 @@
priv->root_dentry = root_dentry;
- debugfs_create_file("fw_debug", 0644, root_dentry, priv,
+ debugfs_create_file("fw_debug", 0600, root_dentry, priv,
&icnss_fw_debug_fops);
- debugfs_create_file("stats", 0644, root_dentry, priv,
+ debugfs_create_file("stats", 0600, root_dentry, priv,
&icnss_stats_fops);
debugfs_create_file("reg_read", 0600, root_dentry, priv,
&icnss_regread_fops);
- debugfs_create_file("reg_write", 0644, root_dentry, priv,
+ debugfs_create_file("reg_write", 0600, root_dentry, priv,
&icnss_regwrite_fops);
out:
return ret;
}
+#else
+static int icnss_debugfs_create(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct dentry *root_dentry;
+
+ root_dentry = debugfs_create_dir("icnss", NULL);
+
+ if (IS_ERR(root_dentry)) {
+ ret = PTR_ERR(root_dentry);
+ icnss_pr_err("Unable to create debugfs %d\n", ret);
+ return ret;
+ }
+
+ priv->root_dentry = root_dentry;
+
+ debugfs_create_file("stats", 0600, root_dentry, priv,
+ &icnss_stats_fops);
+ return 0;
+}
+#endif
static void icnss_debugfs_destroy(struct icnss_priv *priv)
{
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1626892..1cf907e 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -533,6 +533,10 @@
return "4:3";
case HDMI_PICTURE_ASPECT_16_9:
return "16:9";
+ case HDMI_PICTURE_ASPECT_64_27:
+ return "64:27";
+ case HDMI_PICTURE_ASPECT_256_135:
+ return "256:135";
case HDMI_PICTURE_ASPECT_RESERVED:
return "Reserved";
}
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index ac9d7d8..1c12875 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -531,6 +531,20 @@
* @audio_latency: audio latency info from ELD, if found
* @null_edid_counter: track sinks that give us all zeros for the EDID
* @bad_edid_counter: track sinks that give us an EDID with invalid checksum
+ * @max_tmds_char: indicates the maximum TMDS Character Rate supported
+ * @scdc_present: when set the sink supports SCDC functionality
+ * @rr_capable: when set the sink is capable of initiating an SCDC read request
+ * @supports_scramble: when set the sink supports less than 340Mcsc scrambling
+ * @flags_3d: 3D view(s) supported by the sink, see drm_edid.h (DRM_EDID_3D_*)
+ * @pt_scan_info: PT scan info obtained from the VCDB of EDID
+ * @it_scan_info: IT scan info obtained from the VCDB of EDID
+ * @ce_scan_info: CE scan info obtained from the VCDB of EDID
+ * @hdr_eotf: Electro optical transfer function obtained from HDR block
+ * @hdr_metadata_type_one: Metadata type one obtained from HDR block
+ * @hdr_max_luminance: desired max luminance obtained from HDR block
+ * @hdr_avg_luminance: desired avg luminance obtained from HDR block
+ * @hdr_min_luminance: desired min luminance obtained from HDR block
+ * @hdr_supported: does the sink support HDR content
* @edid_corrupt: indicates whether the last read EDID was corrupt
* @debugfs_entry: debugfs directory for this connector
* @state: current atomic state for this connector
@@ -665,6 +679,22 @@
int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
unsigned bad_edid_counter;
+ /* EDID bits HDMI 2.0 */
+ int max_tmds_char; /* in Mcsc */
+ bool scdc_present;
+ bool rr_capable;
+ bool supports_scramble;
+ int flags_3d;
+ u8 pt_scan_info;
+ u8 it_scan_info;
+ u8 ce_scan_info;
+ u32 hdr_eotf;
+ bool hdr_metadata_type_one;
+ u32 hdr_max_luminance;
+ u32 hdr_avg_luminance;
+ u32 hdr_min_luminance;
+ bool hdr_supported;
+
/* Flag for raw EDID header corruption - used in Displayport
* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
*/
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index c3a7d44..32bd104 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -269,6 +269,11 @@
#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad))
+/* HDMI 2.0 */
+#define DRM_EDID_3D_INDEPENDENT_VIEW (1 << 2)
+#define DRM_EDID_3D_DUAL_VIEW (1 << 1)
+#define DRM_EDID_3D_OSD_DISPARITY (1 << 0)
+
struct edid {
u8 header[8];
/* Vendor & product info */
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index e974420..bc38b99a 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -35,6 +35,7 @@
};
#define HDMI_IEEE_OUI 0x000c03
+#define HDMI_IEEE_OUI_HF 0xc45dd8
#define HDMI_INFOFRAME_HEADER_SIZE 4
#define HDMI_AVI_INFOFRAME_SIZE 13
#define HDMI_SPD_INFOFRAME_SIZE 25
@@ -78,6 +79,8 @@
HDMI_PICTURE_ASPECT_NONE,
HDMI_PICTURE_ASPECT_4_3,
HDMI_PICTURE_ASPECT_16_9,
+ HDMI_PICTURE_ASPECT_64_27,
+ HDMI_PICTURE_ASPECT_256_135,
HDMI_PICTURE_ASPECT_RESERVED,
};
diff --git a/include/linux/sde_rsc.h b/include/linux/sde_rsc.h
index f3fa9e6..f921909 100644
--- a/include/linux/sde_rsc.h
+++ b/include/linux/sde_rsc.h
@@ -206,6 +206,23 @@
*/
void sde_rsc_unregister_event(struct sde_rsc_event *event);
+/**
+ * is_sde_rsc_available - check if display rsc available.
+ * @rsc_index: A client will be created on this RSC. As of now only
+ * SDE_RSC_INDEX is valid rsc index.
+ * Returns: true if rsc is available; false in all other cases
+ */
+bool is_sde_rsc_available(int rsc_index);
+
+/**
+ * get_sde_rsc_current_state - gets the current state of sde rsc.
+ * @rsc_index: A client will be created on this RSC. As of now only
+ * SDE_RSC_INDEX is valid rsc index.
+ * Returns: current state if rsc available; SDE_RSC_IDLE_STATE for
+ * all other cases
+ */
+enum sde_rsc_state get_sde_rsc_current_state(int rsc_index);
+
#else
static inline struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index,
@@ -242,6 +259,15 @@
{
}
+static inline bool is_sde_rsc_available(int rsc_index)
+{
+ return false;
+}
+
+static inline enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
+{
+ return SDE_RSC_IDLE_STATE;
+}
#endif /* CONFIG_DRM_SDE_RSC */
#endif /* _SDE_RSC_H_ */
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index f66ba9c..9c927a5 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -77,7 +77,8 @@
#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14)
#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14)
#define DRM_MODE_FLAG_SEAMLESS (1<<19)
-
+#define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20)
+#define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21)
/* DPMS flags */
/* bit compatible with the xorg definitions. */