Merge "sched/debug: Make sysrq prints of sched debug data optional"
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
index e564cd5..2070c64 100644
--- a/Documentation/devicetree/bindings/fb/mdss-edp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -23,6 +23,10 @@
interface is mapped.
- gpio-panel-hpd : gpio pin use for edp hpd
+
+Optional properties:
+- qcom,cont-splash-enabled: Boolean used to enable continuous splash mode.
+
Example:
mdss_edp: qcom,mdss_edp@fd923400 {
compatible = "qcom,mdss-edp";
diff --git a/arch/arm/boot/dts/msm-rdbg.dtsi b/arch/arm/boot/dts/msm-rdbg.dtsi
new file mode 100644
index 0000000..f7f52be
--- /dev/null
+++ b/arch/arm/boot/dts/msm-rdbg.dtsi
@@ -0,0 +1,75 @@
+/* Copyright (c) 2013, 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.
+ */
+
+&soc {
+ smp2pgpio_rdbg_2_in: qcom,smp2pgpio-rdbg-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "rdbg";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_client_rdbg_2_in {
+ compatible = "qcom,smp2pgpio_client_rdbg_2_in";
+ gpios = <&smp2pgpio_rdbg_2_in 0 0>;
+ };
+
+ smp2pgpio_rdbg_2_out: qcom,smp2pgpio-rdbg-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "rdbg";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_client_rdbg_2_out {
+ compatible = "qcom,smp2pgpio_client_rdbg_2_out";
+ gpios = <&smp2pgpio_rdbg_2_out 0 0>;
+ };
+
+ smp2pgpio_rdbg_1_in: qcom,smp2pgpio-rdbg-1-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "rdbg";
+ qcom,remote-pid = <1>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_client_rdbg_1_in {
+ compatible = "qcom,smp2pgpio_client_rdbg_1_in";
+ gpios = <&smp2pgpio_rdbg_1_in 0 0>;
+ };
+
+ smp2pgpio_rdbg_1_out: qcom,smp2pgpio-rdbg-1-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "rdbg";
+ qcom,remote-pid = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_client_rdbg_1_out {
+ compatible = "qcom,smp2pgpio_client_rdbg_1_out";
+ gpios = <&smp2pgpio_rdbg_1_out 0 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 9764982..7eb3f57 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -20,6 +20,7 @@
reg = <0x1700 0x100>;
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1275000>;
+ qcom,mode = "auto";
};
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index c86d3f3..0b8b07e 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -57,6 +57,7 @@
/include/ "msm8226-mdss.dtsi"
/include/ "msm8226-coresight.dtsi"
/include/ "msm8226-iommu-domains.dtsi"
+/include/ "msm-rdbg.dtsi"
&soc {
#address-cells = <1>;
@@ -972,6 +973,9 @@
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <400000>;
qcom,i2c-src-freq = <19200000>;
+ qcom,sda-gpio = <&msmgpio 10 0>;
+ qcom,scl-gpio = <&msmgpio 11 0>;
+ qcom,master-id = <86>;
};
i2c@f9926000 { /* BLSP-1 QUP-4 */
cell-index = <0>;
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 0af6ee7..a40d3aa 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -43,6 +43,7 @@
/include/ "msm8610-smp2p.dtsi"
/include/ "msm8610-bus.dtsi"
/include/ "msm8610-mdss.dtsi"
+/include/ "msm-rdbg.dtsi"
&soc {
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index ba085a0..922e3e0 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -30,6 +30,11 @@
qcom,panel-pwm-period = <53>;
};
+ qcom,mdss_edp@fd923400 {
+ status = "ok";
+ qcom,cont-splash-enabled;
+ };
+
i2c@f9967000 {
battery@b {
compatible = "ti,bq28400-battery";
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 0412c73..fdf5243 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -100,6 +100,7 @@
/include/ "msm8974-mdss.dtsi"
/include/ "msm8974-smp2p.dtsi"
/include/ "msm8974-bus.dtsi"
+/include/ "msm-rdbg.dtsi"
&soc {
#address-cells = <1>;
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index c5aa51a..c47a474 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -457,3 +457,4 @@
CONFIG_TOUCHSCREEN_GT9XX=y
CONFIG_GT9XX_TOUCHPANEL_DRIVER=y
CONFIG_DEFAULT_ROW=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index f4ee78d..a163103 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -511,3 +511,4 @@
CONFIG_TOUCHSCREEN_GT9XX=y
CONFIG_GT9XX_TOUCHPANEL_DRIVER=y
CONFIG_DEFAULT_ROW=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 8bdbc51..a6443ba 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -427,3 +427,4 @@
CONFIG_LOGCAT_SIZE=64
CONFIG_SENSORS_CAPELLA_CM36283=y
CONFIG_NFC_QNCI=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 3ab8cac..80fe046 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -480,3 +480,4 @@
CONFIG_SENSORS_STK3X1X=y
CONFIG_SENSORS_MMA8X5X=y
CONFIG_SENSORS_CAPELLA_CM36283=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 7f59ae4..82f4ee2 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -502,3 +502,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 387308f..9051f8f 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -542,3 +542,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
+CONFIG_MSM_RDBG=m
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index fe533d5..0051578 100755
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -541,7 +541,8 @@
#define gpll1_hsic_source_val 4
#define cxo_lpass_source_val 0
#define gpll0_lpass_source_val 5
-#define edppll_270_mm_source_val 4
+#define edp_mainlink_mm_source_val 4
+#define edp_pixel_mm_source_val 5
#define edppll_350_mm_source_val 4
#define dsipll_750_mm_source_val 1
#define dsipll0_byte_mm_source_val 1
@@ -589,6 +590,17 @@
| BVAL(10, 8, s##_mm_source_val), \
}
+#define F_EDP(f, s, div, m, n) \
+ { \
+ .freq_hz = (f), \
+ .src_clk = &s##_clk_src.c, \
+ .m_val = (m), \
+ .n_val = ~((n)-(m)) * !!(n), \
+ .d_val = ~(n),\
+ .div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+ | BVAL(10, 8, s##_mm_source_val), \
+ }
+
#define F_MDSS(f, s, div, m, n) \
{ \
.freq_hz = (f), \
@@ -3181,40 +3193,42 @@
};
static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = {
- F_MDSS(162000000, edppll_270, 2, 0, 0),
- F_MDSS(270000000, edppll_270, 11, 0, 0),
+ F_EDP(162000000, edp_mainlink, 1, 0, 0),
+ F_EDP(270000000, edp_mainlink, 1, 0, 0),
F_END
};
static struct rcg_clk edplink_clk_src = {
.cmd_rcgr_reg = EDPLINK_CMD_RCGR,
- .set_rate = set_rate_hid,
.freq_tbl = ftbl_mdss_edplink_clk,
.current_freq = &rcg_dummy_freq,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "edplink_clk_src",
- .ops = &clk_ops_rcg,
+ .ops = &clk_ops_rcg_edp,
VDD_DIG_FMAX_MAP2(LOW, 135000000, NOMINAL, 270000000),
CLK_INIT(edplink_clk_src.c),
},
};
-static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
- F_MDSS(138500000, edppll_350, 2, 0, 0),
- F_MDSS(350000000, edppll_350, 11, 0, 0),
+static struct clk_freq_tbl edp_pixel_freq_tbl[] = {
+ {
+ .src_clk = &edp_pixel_clk_src.c,
+ .div_src_val = BVAL(10, 8, edp_pixel_mm_source_val)
+ | BVAL(4, 0, 0),
+ },
F_END
};
static struct rcg_clk edppixel_clk_src = {
.cmd_rcgr_reg = EDPPIXEL_CMD_RCGR,
.set_rate = set_rate_mnd,
- .freq_tbl = ftbl_mdss_edppixel_clk,
- .current_freq = &rcg_dummy_freq,
+ .current_freq = edp_pixel_freq_tbl,
.base = &virt_bases[MMSS_BASE],
.c = {
+ .parent = &edp_pixel_clk_src.c,
.dbg_name = "edppixel_clk_src",
- .ops = &clk_ops_rcg_mnd,
+ .ops = &clk_ops_edppixel,
VDD_DIG_FMAX_MAP2(LOW, 175000000, NOMINAL, 350000000),
CLK_INIT(edppixel_clk_src.c),
},
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index 01ecba2..cc6f290 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -641,6 +641,74 @@
return HANDOFF_ENABLED_CLK;
}
+struct frac_entry {
+ int num;
+ int den;
+};
+
+static struct frac_entry frac_table_675m[] = { /* link rate of 270M */
+ {52, 295}, /* 119 M */
+ {11, 57}, /* 130.25 M */
+ {63, 307}, /* 138.50 M */
+ {11, 50}, /* 148.50 M */
+ {47, 206}, /* 154 M */
+ {31, 100}, /* 205.25 M */
+ {107, 269}, /* 268.50 M */
+ {0, 0},
+};
+
+static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
+ {31, 211}, /* 119 M */
+ {32, 199}, /* 130.25 M */
+ {63, 307}, /* 138.50 M */
+ {11, 60}, /* 148.50 M */
+ {50, 263}, /* 154 M */
+ {31, 120}, /* 205.25 M */
+ {119, 359}, /* 268.50 M */
+ {0, 0},
+};
+
+static int set_rate_edp_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ struct frac_entry *frac;
+ int delta = 100000;
+ s64 request;
+ s64 src_rate;
+
+ src_rate = clk_get_rate(clk->parent);
+
+ if (src_rate == 810000000)
+ frac = frac_table_810m;
+ else
+ frac = frac_table_675m;
+
+ while (frac->num) {
+ request = rate;
+ request *= frac->den;
+ request = div_s64(request, frac->num);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta))) {
+ frac++;
+ continue;
+ }
+
+ pixel_freq->div_src_val &= ~BM(4, 0);
+ if (frac->den == frac->num) {
+ pixel_freq->m_val = 0;
+ pixel_freq->n_val = 0;
+ } else {
+ pixel_freq->m_val = frac->num;
+ pixel_freq->n_val = ~(frac->den - frac->num);
+ pixel_freq->d_val = ~frac->den;
+ }
+ set_rate_mnd(rcg, pixel_freq);
+ return 0;
+ }
+ return -EINVAL;
+}
+
enum handoff byte_rcg_handoff(struct clk *clk)
{
struct rcg_clk *rcg = to_rcg_clk(clk);
@@ -796,6 +864,37 @@
return rc;
}
+static struct clk *edp_clk_get_parent(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ struct clk *clk;
+ struct clk_freq_tbl *freq;
+ uint32_t rate;
+ u32 cmd_rcgr_regval;
+
+ /* Is there a pending configuration? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK)
+ return NULL;
+
+ /* Figure out what rate the rcg is running at */
+ for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) {
+ clk = freq->src_clk;
+ if (clk && clk->ops->get_rate) {
+ rate = clk->ops->get_rate(clk);
+ if (rate == freq->freq_hz)
+ break;
+ }
+ }
+
+ /* No known frequency found */
+ if (freq->freq_hz == FREQ_END)
+ return NULL;
+
+ rcg->current_freq = freq;
+ return freq->src_clk;
+}
+
static DEFINE_SPINLOCK(mux_reg_lock);
@@ -886,6 +985,14 @@
.handoff = pixel_rcg_handoff,
};
+struct clk_ops clk_ops_edppixel = {
+ .enable = rcg_clk_prepare,
+ .set_rate = set_rate_edp_pixel,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = pixel_rcg_handoff,
+};
+
struct clk_ops clk_ops_byte = {
.enable = rcg_clk_prepare,
.set_rate = set_rate_byte,
@@ -903,6 +1010,15 @@
.get_parent = rcg_clk_get_parent,
};
+struct clk_ops clk_ops_rcg_edp = {
+ .enable = rcg_clk_prepare,
+ .set_rate = rcg_clk_set_rate_hdmi,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_clk_handoff,
+ .get_parent = edp_clk_get_parent,
+};
+
struct clk_ops clk_ops_branch = {
.enable = branch_clk_enable,
.disable = branch_clk_disable,
diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h
index 8724f63..35fb6fa 100644
--- a/arch/arm/mach-msm/clock-local2.h
+++ b/arch/arm/mach-msm/clock-local2.h
@@ -174,9 +174,11 @@
extern struct clk_ops clk_ops_branch;
extern struct clk_ops clk_ops_vote;
extern struct clk_ops clk_ops_rcg_hdmi;
+extern struct clk_ops clk_ops_rcg_edp;
extern struct clk_ops clk_ops_byte;
extern struct clk_ops clk_ops_pixel;
extern struct clk_mux_ops mux_reg_ops;
+extern struct clk_ops clk_ops_edppixel;
enum handoff pixel_rcg_handoff(struct clk *clk);
enum handoff byte_rcg_handoff(struct clk *clk);
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 2fa921a..b500e1d 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -37,6 +37,9 @@
#define DSI_PHY_PHYS 0xFD922A00
#define DSI_PHY_SIZE 0x000000D4
+#define EDP_PHY_PHYS 0xFD923A00
+#define EDP_PHY_SIZE 0x000000D4
+
#define HDMI_PHY_PHYS 0xFD922500
#define HDMI_PHY_SIZE 0x0000007C
@@ -153,6 +156,7 @@
static unsigned char *mdss_dsi_base;
static unsigned char *gdsc_base;
static struct clk *mdss_ahb_clk;
+static unsigned char *mdss_edp_base;
static void __iomem *hdmi_phy_base;
static void __iomem *hdmi_phy_pll_base;
@@ -1669,6 +1673,459 @@
},
};
+static inline struct edp_pll_vco_clk *to_edp_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct edp_pll_vco_clk, c);
+}
+
+static int edp_vco_set_rate(struct clk *c, unsigned long vco_rate)
+{
+ struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
+ int rc = 0;
+
+ pr_debug("%s: vco_rate=%d\n", __func__, (int)vco_rate);
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ }
+ if (vco_rate == 810000000) {
+ DSS_REG_W(mdss_edp_base, 0x0c, 0x18);
+ /* UNIPHY_PLL_LKDET_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ /* UNIPHY_PLL_REFCLK_CFG */
+ DSS_REG_W(mdss_edp_base, 0x00, 0x00);
+ /* UNIPHY_PLL_SDM_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x38, 0x36);
+ /* UNIPHY_PLL_SDM_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x3c, 0x69);
+ /* UNIPHY_PLL_SDM_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x40, 0xff);
+ /* UNIPHY_PLL_SDM_CFG3 */
+ DSS_REG_W(mdss_edp_base, 0x44, 0x2f);
+ /* UNIPHY_PLL_SDM_CFG4 */
+ DSS_REG_W(mdss_edp_base, 0x48, 0x00);
+ /* UNIPHY_PLL_SSC_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x4c, 0x80);
+ /* UNIPHY_PLL_SSC_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x50, 0x00);
+ /* UNIPHY_PLL_SSC_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x54, 0x00);
+ /* UNIPHY_PLL_SSC_CFG3 */
+ DSS_REG_W(mdss_edp_base, 0x58, 0x00);
+ /* UNIPHY_PLL_CAL_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ /* UNIPHY_PLL_CAL_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x74, 0x01);
+ /* UNIPHY_PLL_CAL_CFG6 */
+ DSS_REG_W(mdss_edp_base, 0x84, 0x5a);
+ /* UNIPHY_PLL_CAL_CFG7 */
+ DSS_REG_W(mdss_edp_base, 0x88, 0x0);
+ /* UNIPHY_PLL_CAL_CFG8 */
+ DSS_REG_W(mdss_edp_base, 0x8c, 0x60);
+ /* UNIPHY_PLL_CAL_CFG9 */
+ DSS_REG_W(mdss_edp_base, 0x90, 0x0);
+ /* UNIPHY_PLL_CAL_CFG10 */
+ DSS_REG_W(mdss_edp_base, 0x94, 0x2a);
+ /* UNIPHY_PLL_CAL_CFG11 */
+ DSS_REG_W(mdss_edp_base, 0x98, 0x3);
+ /* UNIPHY_PLL_LKDET_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x5c, 0x10);
+ /* UNIPHY_PLL_LKDET_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x60, 0x1a);
+ /* UNIPHY_PLL_POSTDIV1_CFG */
+ DSS_REG_W(mdss_edp_base, 0x04, 0x00);
+ /* UNIPHY_PLL_POSTDIV3_CFG */
+ DSS_REG_W(mdss_edp_base, 0x28, 0x00);
+ } else if (vco_rate == 1350000000) {
+ /* UNIPHY_PLL_LKDET_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ /* UNIPHY_PLL_REFCLK_CFG */
+ DSS_REG_W(mdss_edp_base, 0x00, 0x01);
+ /* UNIPHY_PLL_SDM_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x38, 0x36);
+ /* UNIPHY_PLL_SDM_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x3c, 0x62);
+ /* UNIPHY_PLL_SDM_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x40, 0x00);
+ /* UNIPHY_PLL_SDM_CFG3 */
+ DSS_REG_W(mdss_edp_base, 0x44, 0x28);
+ /* UNIPHY_PLL_SDM_CFG4 */
+ DSS_REG_W(mdss_edp_base, 0x48, 0x00);
+ /* UNIPHY_PLL_SSC_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x4c, 0x80);
+ /* UNIPHY_PLL_SSC_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x50, 0x00);
+ /* UNIPHY_PLL_SSC_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x54, 0x00);
+ /* UNIPHY_PLL_SSC_CFG3 */
+ DSS_REG_W(mdss_edp_base, 0x58, 0x00);
+ /* UNIPHY_PLL_CAL_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ /* UNIPHY_PLL_CAL_CFG2 */
+ DSS_REG_W(mdss_edp_base, 0x74, 0x01);
+ /* UNIPHY_PLL_CAL_CFG6 */
+ DSS_REG_W(mdss_edp_base, 0x84, 0x5a);
+ /* UNIPHY_PLL_CAL_CFG7 */
+ DSS_REG_W(mdss_edp_base, 0x88, 0x0);
+ /* UNIPHY_PLL_CAL_CFG8 */
+ DSS_REG_W(mdss_edp_base, 0x8c, 0x60);
+ /* UNIPHY_PLL_CAL_CFG9 */
+ DSS_REG_W(mdss_edp_base, 0x90, 0x0);
+ /* UNIPHY_PLL_CAL_CFG10 */
+ DSS_REG_W(mdss_edp_base, 0x94, 0x46);
+ /* UNIPHY_PLL_CAL_CFG11 */
+ DSS_REG_W(mdss_edp_base, 0x98, 0x5);
+ /* UNIPHY_PLL_LKDET_CFG0 */
+ DSS_REG_W(mdss_edp_base, 0x5c, 0x10);
+ /* UNIPHY_PLL_LKDET_CFG1 */
+ DSS_REG_W(mdss_edp_base, 0x60, 0x1a);
+ /* UNIPHY_PLL_POSTDIV1_CFG */
+ DSS_REG_W(mdss_edp_base, 0x04, 0x00);
+ /* UNIPHY_PLL_POSTDIV3_CFG */
+ DSS_REG_W(mdss_edp_base, 0x28, 0x00);
+ } else {
+ pr_err("%s: rate=%d is NOT supported\n", __func__,
+ (int)vco_rate);
+ vco_rate = 0;
+ rc = -EINVAL;
+ }
+
+ DSS_REG_W(mdss_edp_base, 0x20, 0x01); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x05); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x07); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x0f); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ mdss_ahb_clk_enable(0);
+
+ vco->rate = vco_rate;
+
+ return rc;
+}
+
+static int edp_pll_ready_poll(void)
+{
+ int cnt;
+ u32 status;
+
+ /* ahb clock should be enabled by caller */
+ cnt = 100;
+ while (cnt--) {
+ udelay(100);
+ status = DSS_REG_R(mdss_edp_base, 0xc0);
+ status &= 0x01;
+ if (status)
+ break;
+ }
+ pr_debug("%s: cnt=%d status=%d\n", __func__, cnt, (int)status);
+
+ if (status)
+ return 1;
+
+ pr_err("%s: PLL NOT ready\n", __func__);
+ return 0;
+}
+
+static int edp_vco_enable(struct clk *c)
+{
+ int i, ready;
+ int rc = 0;
+
+ if (!mdss_gdsc_enabled()) {
+ pr_err("%s: mdss GDSC is not enabled\n", __func__);
+ return -EPERM;
+ }
+
+ /* called from enable, irq disable. can not call clk_prepare */
+ rc = clk_enable(mdss_ahb_clk);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ready = edp_pll_ready_poll();
+ if (ready)
+ break;
+ DSS_REG_W(mdss_edp_base, 0x20, 0x01); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x05); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x07); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ DSS_REG_W(mdss_edp_base, 0x20, 0x0f); /* UNIPHY_PLL_GLB_CFG */
+ udelay(100);
+ }
+ clk_disable(mdss_ahb_clk);
+
+ if (ready) {
+ pr_debug("%s: EDP PLL locked\n", __func__);
+ return 0;
+ }
+
+ pr_err("%s: EDP PLL failed to lock\n", __func__);
+ return -EINVAL;
+}
+
+static void edp_vco_disable(struct clk *c)
+{
+ int rc = 0;
+
+ if (!mdss_gdsc_enabled()) {
+ pr_err("%s: mdss GDSC is not enabled\n", __func__);
+ return;
+ }
+
+ /* called from unprepare which is not atomic */
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return;
+ }
+
+ DSS_REG_W(mdss_edp_base, 0x20, 0x00);
+
+ mdss_ahb_clk_enable(0);
+
+ pr_debug("%s: EDP PLL Disabled\n", __func__);
+ return;
+}
+
+static unsigned long edp_vco_get_rate(struct clk *c)
+{
+ struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
+ u32 pll_status, div2;
+ int rc;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ if (vco->rate == 0) {
+ pll_status = DSS_REG_R(mdss_edp_base, 0xc0);
+ if (pll_status & 0x01) {
+ div2 = DSS_REG_R(mdss_edp_base, 0x24);
+ if (div2 & 0x01)
+ vco->rate = 1350000000;
+ else
+ vco->rate = 810000000;
+ }
+ }
+ mdss_ahb_clk_enable(0);
+
+ pr_debug("%s: rate=%d\n", __func__, (int)vco->rate);
+
+ return vco->rate;
+}
+
+static long edp_vco_round_rate(struct clk *c, unsigned long rate)
+{
+ struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
+ unsigned long rrate = -ENOENT;
+ unsigned long *lp;
+
+ lp = vco->rate_list;
+ while (*lp) {
+ rrate = *lp;
+ if (rate <= rrate)
+ break;
+ lp++;
+ }
+
+ pr_debug("%s: rrate=%d\n", __func__, (int)rrate);
+
+ return rrate;
+}
+
+static int edp_vco_prepare(struct clk *c)
+{
+ struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
+
+ pr_debug("%s: rate=%d\n", __func__, (int)vco->rate);
+
+ return edp_vco_set_rate(c, vco->rate);
+}
+
+static void edp_vco_unprepare(struct clk *c)
+{
+ struct edp_pll_vco_clk *vco = to_edp_vco_clk(c);
+
+ pr_debug("%s: rate=%d\n", __func__, (int)vco->rate);
+
+ edp_vco_disable(c);
+}
+
+static int edp_pll_lock_status(void)
+{
+ u32 status;
+ int pll_locked = 0;
+ int rc;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_noirq((mdss_edp_base + 0xc0),
+ status, ((status & BIT(0)) == 1),
+ PLL_POLL_MAX_READS, PLL_POLL_TIMEOUT_US)) {
+ pr_debug("%s: EDP PLL status=%x failed to Lock\n",
+ __func__, status);
+ pll_locked = 0;
+ } else {
+ pll_locked = 1;
+ }
+ mdss_ahb_clk_enable(0);
+
+ return pll_locked;
+}
+
+static enum handoff edp_vco_handoff(struct clk *c)
+{
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+
+ if (edp_pll_lock_status()) {
+ c->rate = edp_vco_get_rate(c);
+ ret = HANDOFF_ENABLED_CLK;
+ }
+
+ pr_debug("%s: done, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+/* edp vco rate */
+static unsigned long edp_vco_rate_list[] = {
+ 810000000, 1350000000, 0};
+
+struct clk_ops edp_vco_clk_ops = {
+ .enable = edp_vco_enable,
+ .set_rate = edp_vco_set_rate,
+ .get_rate = edp_vco_get_rate,
+ .round_rate = edp_vco_round_rate,
+ .prepare = edp_vco_prepare,
+ .unprepare = edp_vco_unprepare,
+ .handoff = edp_vco_handoff,
+};
+
+struct edp_pll_vco_clk edp_vco_clk = {
+ .ref_clk_rate = 19200000,
+ .rate = 0,
+ .rate_list = edp_vco_rate_list,
+ .c = {
+ .dbg_name = "edp_vco_clk",
+ .ops = &edp_vco_clk_ops,
+ CLK_INIT(edp_vco_clk.c),
+ },
+};
+
+static unsigned long edp_mainlink_get_rate(struct clk *c)
+{
+ struct div_clk *mclk = to_div_clk(c);
+ struct clk *pclk;
+ unsigned long rate = 0;
+
+ pclk = clk_get_parent(c);
+
+ if (pclk->ops->get_rate) {
+ rate = pclk->ops->get_rate(pclk);
+ rate /= mclk->data.div;
+ }
+
+ pr_debug("%s: rate=%d div=%d\n", __func__, (int)rate, mclk->data.div);
+
+ return rate;
+}
+
+static struct clk_ops edp_mainlink_clk_src_ops;
+static struct clk_div_ops fixed_5div_ops; /* null ops */
+
+struct div_clk edp_mainlink_clk_src = {
+ .ops = &fixed_5div_ops,
+ .data = {
+ .div = 5,
+ },
+ .c = {
+ .parent = &edp_vco_clk.c,
+ .dbg_name = "edp_mainlink_clk_src",
+ .ops = &edp_mainlink_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(edp_mainlink_clk_src.c),
+ }
+};
+
+
+static struct clk_ops edp_pixel_clk_ops;
+
+/*
+ * this rate is from pll to clock controller
+ * output from pll to CC has two possibilities
+ * 1: if mainlink rate is 270M, then 675M
+ * 2: if mainlink rate is 162M, then 810M
+ */
+static int edp_pixel_set_div(struct div_clk *clk, int div)
+{
+ int rc = 0;
+
+ rc = mdss_ahb_clk_enable(1);
+ if (rc) {
+ pr_err("%s: failed to enable mdss ahb clock. rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ pr_debug("%s: div=%d\n", __func__, div);
+ DSS_REG_W(mdss_edp_base, 0x24, (div - 1)); /* UNIPHY_PLL_POSTDIV2_CFG */
+
+ mdss_ahb_clk_enable(0);
+ return 0;
+}
+
+static int edp_pixel_get_div(struct div_clk *clk)
+{
+ int div = 0;
+
+ if (mdss_ahb_clk_enable(1)) {
+ pr_debug("%s: Failed to enable mdss ahb clock\n", __func__);
+ return 1;
+ }
+ div = DSS_REG_R(mdss_edp_base, 0x24); /* UNIPHY_PLL_POSTDIV2_CFG */
+ div &= 0x01;
+ pr_debug("%s: div=%d\n", __func__, div);
+ mdss_ahb_clk_enable(0);
+ return div + 1;
+}
+
+static struct clk_div_ops edp_pixel_ops = {
+ .set_div = edp_pixel_set_div,
+ .get_div = edp_pixel_get_div,
+};
+
+struct div_clk edp_pixel_clk_src = {
+ .data = {
+ .max_div = 2,
+ .min_div = 1,
+ },
+ .ops = &edp_pixel_ops,
+ .c = {
+ .parent = &edp_vco_clk.c,
+ .dbg_name = "edp_pixel_clk_src",
+ .ops = &edp_pixel_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(edp_pixel_clk_src.c),
+ },
+};
+
void __init mdss_clk_ctrl_pre_init(struct clk *ahb_clk)
{
BUG_ON(ahb_clk == NULL);
@@ -1691,6 +2148,10 @@
if (!hdmi_phy_pll_base)
pr_err("%s: unable to ioremap hdmi phy pll base", __func__);
+ mdss_edp_base = ioremap(EDP_PHY_PHYS, EDP_PHY_SIZE);
+ if (!mdss_edp_base)
+ pr_err("%s: unable to remap edp base", __func__);
+
pixel_clk_src_ops = clk_ops_slave_div;
pixel_clk_src_ops.prepare = div_prepare;
@@ -1702,5 +2163,11 @@
byte_mux_clk_ops = clk_ops_gen_mux;
byte_mux_clk_ops.prepare = mux_prepare;
-}
+ edp_mainlink_clk_src_ops = clk_ops_div;
+ edp_mainlink_clk_src_ops.get_parent = clk_get_parent;
+ edp_mainlink_clk_src_ops.get_rate = edp_mainlink_get_rate;
+
+ edp_pixel_clk_ops = clk_ops_slave_div;
+ edp_pixel_clk_ops.prepare = div_prepare;
+}
diff --git a/arch/arm/mach-msm/clock-mdss-8974.h b/arch/arm/mach-msm/clock-mdss-8974.h
index 9fd3026..da24b0d 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.h
+++ b/arch/arm/mach-msm/clock-mdss-8974.h
@@ -26,6 +26,14 @@
void hdmi_pll_disable(void);
int hdmi_pll_set_rate(unsigned long rate);
+struct edp_pll_vco_clk {
+ unsigned long ref_clk_rate;
+ unsigned long rate; /* vco rate */
+ unsigned long *rate_list;
+
+ struct clk c;
+};
+
struct lpfr_cfg {
unsigned long vco_rate;
u32 r;
@@ -57,4 +65,7 @@
extern struct mux_clk byte_mux_8226;
extern struct div_clk byte_clk_src_8226;
+extern struct div_clk edp_mainlink_clk_src;
+extern struct div_clk edp_pixel_clk_src;
+
#endif
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
index faf774f..7995e9a 100644
--- a/arch/arm/mach-msm/rpm-regulator-smd.c
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -864,7 +864,7 @@
load_mA = params[RPM_REGULATOR_PARAM_CURRENT].max;
rpm_vreg_lock(reg->rpm_vreg);
- RPM_VREG_SET_PARAM(reg, CURRENT, MICRO_TO_MILLI(load_uA));
+ RPM_VREG_SET_PARAM(reg, CURRENT, load_mA);
rpm_vreg_unlock(reg->rpm_vreg);
return (load_uA >= reg->rpm_vreg->hpm_min_load)
@@ -917,7 +917,6 @@
if (priv_reg == NULL) {
vreg_err(framework_reg, "could not allocate memory for "
"regulator\n");
- rpm_vreg_unlock(rpm_vreg);
return ERR_PTR(-ENOMEM);
}
@@ -930,7 +929,6 @@
vreg_err(framework_reg, "could not allocate memory for "
"regulator_dev\n");
kfree(priv_reg);
- rpm_vreg_unlock(rpm_vreg);
return ERR_PTR(-ENOMEM);
}
priv_reg->rdev->reg_data = priv_reg;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 02d76fc..97b1f39 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -680,6 +680,13 @@
help
Enter device id for targeted sdio device, this may be overwritten by
module parameters.
-.
+
+config MSM_RDBG
+ tristate "Qualcomm Remote debug driver"
+ depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2
+ help
+ Implements a shared memory based transport mechanism that allows
+ for a debugger running on a host PC to communicate with a remote
+ stub running on peripheral subsystems such as the ADSP, MODEM etc.
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 8032f0b..7589946 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -68,3 +68,4 @@
obj-$(CONFIG_MMC_GENERIC_CSDIO) += csdio.o
obj-$(CONFIG_DIAG_CHAR) += diag/
obj-$(CONFIG_MSM_ADSPRPC) += adsprpc.o
+obj-$(CONFIG_MSM_RDBG) += rdbg.o
diff --git a/drivers/char/rdbg.c b/drivers/char/rdbg.c
new file mode 100644
index 0000000..dbbf4b0
--- /dev/null
+++ b/drivers/char/rdbg.c
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/of_gpio.h>
+#include <linux/mutex.h>
+#include <mach/msm_smsm.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#define SMP2P_NUM_PROCS 8
+
+#define SM_VERSION 1
+#define SM_BLOCKSIZE 128
+
+#define SMQ_MAGIC_INIT 0xFF00FF00
+#define SMQ_MAGIC_PRODUCER (SMQ_MAGIC_INIT | 0x1)
+#define SMQ_MAGIC_CONSUMER (SMQ_MAGIC_INIT | 0x2)
+
+enum SMQ_STATUS {
+ SMQ_SUCCESS = 0,
+ SMQ_ENOMEMORY = -1,
+ SMQ_EBADPARM = -2,
+ SMQ_UNDERFLOW = -3,
+ SMQ_OVERFLOW = -4
+};
+
+enum smq_type {
+ PRODUCER = 1,
+ CONSUMER = 2,
+ INVALID = 3
+};
+
+struct smq_block_map {
+ uint32_t index_read;
+ uint32_t num_blocks;
+ uint8_t *map;
+};
+
+struct smq_node {
+ uint16_t index_block;
+ uint16_t num_blocks;
+} __attribute__ ((__packed__));
+
+struct smq_hdr {
+ uint8_t producer_version;
+ uint8_t consumer_version;
+} __attribute__ ((__packed__));
+
+struct smq_out_state {
+ uint32_t init;
+ uint32_t index_check_queue_for_reset;
+ uint32_t index_sent_write;
+ uint32_t index_free_read;
+} __attribute__ ((__packed__));
+
+struct smq_out {
+ struct smq_out_state s;
+ struct smq_node sent[1];
+};
+
+struct smq_in_state {
+ uint32_t init;
+ uint32_t index_check_queue_for_reset_ack;
+ uint32_t index_sent_read;
+ uint32_t index_free_write;
+} __attribute__ ((__packed__));
+
+struct smq_in {
+ struct smq_in_state s;
+ struct smq_node free[1];
+};
+
+struct smq {
+ struct smq_hdr *hdr;
+ struct smq_out *out;
+ struct smq_in *in;
+ uint8_t *blocks;
+ uint32_t num_blocks;
+ struct mutex *lock;
+ uint32_t initialized;
+ struct smq_block_map block_map;
+ enum smq_type type;
+};
+
+struct gpio_info {
+ int gpio_base_id;
+ int irq_base_id;
+};
+
+struct rdbg_data {
+ struct device *device;
+ struct completion work;
+ struct gpio_info in;
+ struct gpio_info out;
+ bool device_initialized;
+ int gpio_out_offset;
+ bool device_opened;
+ void *smem_addr;
+ size_t smem_size;
+ struct smq producer_smrb;
+ struct smq consumer_smrb;
+ struct mutex write_mutex;
+};
+
+struct rdbg_device {
+ struct cdev cdev;
+ struct class *class;
+ dev_t dev_no;
+ int num_devices;
+ struct rdbg_data *rdbg_data;
+};
+
+static struct rdbg_device g_rdbg_instance = {
+ { {0} },
+ NULL,
+ 0,
+ SMP2P_NUM_PROCS,
+ NULL
+};
+
+struct processor_specific_info {
+ char *name;
+ unsigned int smem_buffer_addr;
+ size_t smem_buffer_size;
+};
+
+static struct processor_specific_info proc_info[SMP2P_NUM_PROCS] = {
+ {0}, /*APPS*/
+ {"rdbg_modem", 0, 0}, /*MODEM*/
+ {"rdbg_adsp", SMEM_LC_DEBUGGER, 16*1024}, /*ADSP*/
+ {0}, /*SMP2P_RESERVED_PROC_1*/
+ {"rdbg_wcnss", 0, 0}, /*WCNSS*/
+ {0}, /*SMP2P_RESERVED_PROC_2*/
+ {0}, /*SMP2P_POWER_PROC*/
+ {0} /*SMP2P_REMOTE_MOCK_PROC*/
+};
+
+static int smq_blockmap_get(struct smq_block_map *block_map,
+ uint32_t *block_index, uint32_t n)
+{
+ uint32_t start;
+ uint32_t mark = 0;
+ uint32_t found = 0;
+ uint32_t i = 0;
+
+ start = block_map->index_read;
+
+ if (n == 1) {
+ do {
+ if (!block_map->map[block_map->index_read]) {
+ *block_index = block_map->index_read;
+ block_map->map[block_map->index_read] = 1;
+ block_map->index_read++;
+ block_map->index_read %= block_map->num_blocks;
+ return SMQ_SUCCESS;
+ }
+ block_map->index_read++;
+ } while (start != (block_map->index_read %=
+ block_map->num_blocks));
+ } else {
+ mark = block_map->num_blocks;
+
+ do {
+ if (!block_map->map[block_map->index_read]) {
+ if (mark > block_map->index_read) {
+ mark = block_map->index_read;
+ start = block_map->index_read;
+ found = 0;
+ }
+
+ found++;
+ if (found == n) {
+ *block_index = mark;
+ for (i = 0; i < n; i++)
+ block_map->map[mark + i] =
+ (uint8_t)(n - i);
+ block_map->index_read += block_map->map
+ [block_map->index_read] - 1;
+ return SMQ_SUCCESS;
+ }
+ } else {
+ found = 0;
+ block_map->index_read += block_map->map
+ [block_map->index_read] - 1;
+ mark = block_map->num_blocks;
+ }
+ block_map->index_read++;
+ } while (start != (block_map->index_read %=
+ block_map->num_blocks));
+ }
+
+ return SMQ_ENOMEMORY;
+}
+
+static void smq_blockmap_put(struct smq_block_map *block_map, uint32_t i)
+{
+ uint32_t num_blocks = block_map->map[i];
+
+ while (num_blocks--) {
+ block_map->map[i] = 0;
+ i++;
+ }
+}
+
+static int smq_blockmap_reset(struct smq_block_map *block_map)
+{
+ if (!block_map->map)
+ return SMQ_ENOMEMORY;
+ memset(block_map->map, 0 , block_map->num_blocks + 1);
+ block_map->index_read = 0;
+
+ return SMQ_SUCCESS;
+}
+
+static int smq_blockmap_ctor(struct smq_block_map *block_map,
+ uint32_t num_blocks)
+{
+ if (num_blocks <= 1)
+ return SMQ_ENOMEMORY;
+
+ block_map->map = kcalloc(num_blocks, sizeof(uint8_t), GFP_KERNEL);
+ if (!block_map->map)
+ return SMQ_ENOMEMORY;
+
+ block_map->num_blocks = num_blocks - 1;
+ smq_blockmap_reset(block_map);
+
+ return SMQ_SUCCESS;
+}
+
+static void smq_blockmap_dtor(struct smq_block_map *block_map)
+{
+ kfree(block_map->map);
+ block_map->map = NULL;
+}
+
+static int smq_free(struct smq *smq, void *data)
+{
+ struct smq_node node;
+ uint32_t index_block;
+ int err = SMQ_SUCCESS;
+
+ if (smq->lock)
+ mutex_lock(smq->lock);
+
+ if ((SM_VERSION != smq->hdr->producer_version) &&
+ (SMQ_MAGIC_PRODUCER != smq->out->s.init)) {
+ err = SMQ_UNDERFLOW;
+ goto bail;
+ }
+
+ index_block = ((uint8_t *)data - smq->blocks) / SM_BLOCKSIZE;
+ if (index_block >= smq->num_blocks) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ node.index_block = (uint16_t)index_block;
+ node.num_blocks = 0;
+ *((struct smq_node *)(smq->in->free + smq->in->
+ s.index_free_write)) = node;
+
+ smq->in->s.index_free_write = (smq->in->s.index_free_write + 1)
+ % smq->num_blocks;
+
+bail:
+ if (smq->lock)
+ mutex_unlock(smq->lock);
+ return err;
+}
+
+static int smq_receive(struct smq *smq, void **pp, int *pnsize, int *pbmore)
+{
+ struct smq_node *node;
+ int err = SMQ_SUCCESS;
+ int more = 0;
+
+ if ((SM_VERSION != smq->hdr->producer_version) &&
+ (SMQ_MAGIC_PRODUCER != smq->out->s.init))
+ return SMQ_UNDERFLOW;
+
+ if (smq->in->s.index_sent_read == smq->out->s.index_sent_write) {
+ err = SMQ_UNDERFLOW;
+ goto bail;
+ }
+
+ node = (struct smq_node *)(smq->out->sent + smq->in->s.index_sent_read);
+ if (node->index_block >= smq->num_blocks) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ smq->in->s.index_sent_read = (smq->in->s.index_sent_read + 1)
+ % smq->num_blocks;
+
+ *pp = smq->blocks + (node->index_block * SM_BLOCKSIZE);
+ *pnsize = SM_BLOCKSIZE * node->num_blocks;
+ rmb();
+ if (smq->in->s.index_sent_read != smq->out->s.index_sent_write)
+ more = 1;
+
+bail:
+ *pbmore = more;
+ return err;
+}
+
+static int smq_alloc_send(struct smq *smq, const uint8_t *pcb, int nsize)
+{
+ void *pv = 0;
+ int num_blocks;
+ uint32_t index_block = 0;
+ int err = SMQ_SUCCESS;
+ struct smq_node *node = NULL;
+
+ mutex_lock(smq->lock);
+
+ if ((SMQ_MAGIC_CONSUMER == smq->in->s.init) &&
+ (SM_VERSION == smq->hdr->consumer_version)) {
+ if (smq->out->s.index_check_queue_for_reset ==
+ smq->in->s.index_check_queue_for_reset_ack) {
+ while (smq->out->s.index_free_read !=
+ smq->in->s.index_free_write) {
+ node = (struct smq_node *)(
+ smq->in->free +
+ smq->out->s.index_free_read);
+ if (node->index_block >= smq->num_blocks) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ smq->out->s.index_free_read =
+ (smq->out->s.index_free_read + 1)
+ % smq->num_blocks;
+
+ smq_blockmap_put(&smq->block_map,
+ node->index_block);
+ rmb();
+ }
+ }
+ }
+
+ num_blocks = ALIGN(nsize, SM_BLOCKSIZE)/SM_BLOCKSIZE;
+ err = smq_blockmap_get(&smq->block_map, &index_block, num_blocks);
+ if (SMQ_SUCCESS != err)
+ goto bail;
+
+ pv = smq->blocks + (SM_BLOCKSIZE * index_block);
+
+ err = copy_from_user((void *)pv, (void *)pcb, nsize);
+ if (0 != err)
+ goto bail;
+
+ ((struct smq_node *)(smq->out->sent +
+ smq->out->s.index_sent_write))->index_block
+ = (uint16_t)index_block;
+ ((struct smq_node *)(smq->out->sent +
+ smq->out->s.index_sent_write))->num_blocks
+ = (uint16_t)num_blocks;
+
+ smq->out->s.index_sent_write = (smq->out->s.index_sent_write + 1)
+ % smq->num_blocks;
+
+bail:
+ if (SMQ_SUCCESS != err) {
+ if (pv)
+ smq_blockmap_put(&smq->block_map, index_block);
+ }
+ mutex_unlock(smq->lock);
+ return err;
+}
+
+static int smq_reset_producer_queue_internal(struct smq *smq,
+ uint32_t reset_num)
+{
+ int retval = 0;
+ uint32_t i;
+
+ if (PRODUCER != smq->type)
+ goto bail;
+
+ mutex_lock(smq->lock);
+ if (smq->out->s.index_check_queue_for_reset != reset_num) {
+ smq->out->s.index_check_queue_for_reset = reset_num;
+ for (i = 0; i < smq->num_blocks; i++)
+ (smq->out->sent + i)->index_block = 0xFFFF;
+
+ smq_blockmap_reset(&smq->block_map);
+ smq->out->s.index_sent_write = 0;
+ smq->out->s.index_free_read = 0;
+ retval = 1;
+ }
+ mutex_unlock(smq->lock);
+
+bail:
+ return retval;
+}
+
+static int smq_check_queue_reset(struct smq *p_cons, struct smq *p_prod)
+{
+ int retval = 0;
+ uint32_t reset_num, i;
+
+ if ((CONSUMER != p_cons->type) ||
+ (SMQ_MAGIC_PRODUCER != p_cons->out->s.init) ||
+ (SM_VERSION != p_cons->hdr->producer_version))
+ goto bail;
+
+ reset_num = p_cons->out->s.index_check_queue_for_reset;
+ if (p_cons->in->s.index_check_queue_for_reset_ack != reset_num) {
+ p_cons->in->s.index_check_queue_for_reset_ack = reset_num;
+ for (i = 0; i < p_cons->num_blocks; i++)
+ (p_cons->in->free + i)->index_block = 0xFFFF;
+
+ p_cons->in->s.index_sent_read = 0;
+ p_cons->in->s.index_free_write = 0;
+
+ retval = smq_reset_producer_queue_internal(p_prod, reset_num);
+ }
+
+bail:
+ return retval;
+}
+
+static int check_subsystem_debug_enabled(void *base_addr, int size)
+{
+ int num_blocks;
+ uint8_t *pb_orig;
+ uint8_t *pb;
+ struct smq smq;
+ int err = 0;
+
+ pb = pb_orig = (uint8_t *)base_addr;
+ pb += sizeof(struct smq_hdr);
+ pb = PTR_ALIGN(pb, 8);
+ size -= pb - (uint8_t *)pb_orig;
+ num_blocks = (int)((size - sizeof(struct smq_out_state) -
+ sizeof(struct smq_in_state))/(SM_BLOCKSIZE +
+ sizeof(struct smq_node) * 2));
+ if (0 >= num_blocks) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ pb += num_blocks * SM_BLOCKSIZE;
+ smq.out = (struct smq_out *)pb;
+ pb += sizeof(struct smq_out_state) + (num_blocks *
+ sizeof(struct smq_node));
+ smq.in = (struct smq_in *)pb;
+
+ if (SMQ_MAGIC_CONSUMER != smq.in->s.init) {
+ pr_err("%s, smq in consumer not initialized", __func__);
+ err = -ECOMM;
+ }
+
+bail:
+ return err;
+}
+
+static void smq_dtor(struct smq *smq)
+{
+ if (SMQ_MAGIC_INIT == smq->initialized) {
+ switch (smq->type) {
+ case PRODUCER:
+ smq->out->s.init = 0;
+ smq_blockmap_dtor(&smq->block_map);
+ break;
+ case CONSUMER:
+ smq->in->s.init = 0;
+ break;
+ default:
+ case INVALID:
+ break;
+ }
+
+ smq->initialized = 0;
+ }
+}
+
+/*
+ * The shared memory is used as a circular ring buffer in each direction.
+ * Thus we have a bi-directional shared memory channel between the AP
+ * and a subsystem. We call this SMQ. Each memory channel contains a header,
+ * data and a control mechanism that is used to synchronize read and write
+ * of data between the AP and the remote subsystem.
+ *
+ * Overall SMQ memory view:
+ *
+ * +------------------------------------------------+
+ * | SMEM buffer |
+ * |-----------------------+------------------------|
+ * |Producer: LA | Producer: Remote |
+ * |Consumer: Remote | subsystem |
+ * | subsystem | Consumer: LA |
+ * | | |
+ * | Producer| Consumer|
+ * +-----------------------+------------------------+
+ * | |
+ * | |
+ * | +--------------------------------------+
+ * | |
+ * | |
+ * v v
+ * +--------------------------------------------------------------+
+ * | Header | Data | Control |
+ * +-----------+---+---+---+-----+----+--+--+-----+---+--+--+-----+
+ * | | b | b | b | | S |n |n | | S |n |n | |
+ * | Producer | l | l | l | | M |o |o | | M |o |o | |
+ * | Ver | o | o | o | | Q |d |d | | Q |d |d | |
+ * |-----------| c | c | c | ... | |e |e | ... | |e |e | ... |
+ * | | k | k | k | | O | | | | I | | | |
+ * | Consumer | | | | | u |0 |1 | | n |0 |1 | |
+ * | Ver | 0 | 1 | 2 | | t | | | | | | | |
+ * +-----------+---+---+---+-----+----+--+--+-----+---+--+--+-----+
+ * | |
+ * + |
+ * |
+ * +------------------------+
+ * |
+ * v
+ * +----+----+----+----+
+ * | SMQ Nodes |
+ * |----|----|----|----|
+ * Node # | 0 | 1 | 2 | ...|
+ * |----|----|----|----|
+ * Starting Block Index # | 0 | 3 | 8 | ...|
+ * |----|----|----|----|
+ * # of blocks | 3 | 5 | 1 | ...|
+ * +----+----+----+----+
+ *
+ * Header: Contains version numbers for software compatibility to ensure
+ * that both producers and consumers on the AP and subsystems know how to
+ * read from and write to the queue.
+ * Both the producer and consumer versions are 1.
+ * +---------+-------------------+
+ * | Size | Field |
+ * +---------+-------------------+
+ * | 1 byte | Producer Version |
+ * +---------+-------------------+
+ * | 1 byte | Consumer Version |
+ * +---------+-------------------+
+ *
+ * Data: The data portion contains multiple blocks [0..N] of a fixed size.
+ * The block size SM_BLOCKSIZE is fixed to 128 bytes for header version #1.
+ * Payload sent from the debug agent app is split (if necessary) and placed
+ * in these blocks. The first data block is placed at the next 8 byte aligned
+ * address after the header.
+ *
+ * The number of blocks for a given SMEM allocation is derived as follows:
+ * Number of Blocks = ((Total Size - Alignment - Size of Header
+ * - Size of SMQIn - Size of SMQOut)/(SM_BLOCKSIZE))
+ *
+ * The producer maintains a private block map of each of these blocks to
+ * determine which of these blocks in the queue is available and which are free.
+ *
+ * Control:
+ * The control portion contains a list of nodes [0..N] where N is number
+ * of available data blocks. Each node identifies the data
+ * block indexes that contain a particular debug message to be transfered,
+ * and the number of blocks it took to hold the contents of the message.
+ *
+ * Each node has the following structure:
+ * +---------+-------------------+
+ * | Size | Field |
+ * +---------+-------------------+
+ * | 2 bytes |Staring Block Index|
+ * +---------+-------------------+
+ * | 2 bytes |Number of Blocks |
+ * +---------+-------------------+
+ *
+ * The producer and the consumer update different parts of the control channel
+ * (SMQOut / SMQIn) respectively. Each of these control data structures contains
+ * information about the last node that was written / read, and the actual nodes
+ * that were written/read.
+ *
+ * SMQOut Structure (R/W by producer, R by consumer):
+ * +---------+-------------------+
+ * | Size | Field |
+ * +---------+-------------------+
+ * | 4 bytes | Magic Init Number |
+ * +---------+-------------------+
+ * | 4 bytes | Reset |
+ * +---------+-------------------+
+ * | 4 bytes | Last Sent Index |
+ * +---------+-------------------+
+ * | 4 bytes | Index Free Read |
+ * +---------+-------------------+
+ *
+ * SMQIn Structure (R/W by consumer, R by producer):
+ * +---------+-------------------+
+ * | Size | Field |
+ * +---------+-------------------+
+ * | 4 bytes | Magic Init Number |
+ * +---------+-------------------+
+ * | 4 bytes | Reset ACK |
+ * +---------+-------------------+
+ * | 4 bytes | Last Read Index |
+ * +---------+-------------------+
+ * | 4 bytes | Index Free Write |
+ * +---------+-------------------+
+ *
+ * Magic Init Number:
+ * Both SMQ Out and SMQ In initialize this field with a predefined magic
+ * number so as to make sure that both the consumer and producer blocks
+ * have fully initialized and have valid data in the shared memory control area.
+ * Producer Magic #: 0xFF00FF01
+ * Consumer Magic #: 0xFF00FF02
+ */
+static int smq_ctor(struct smq *smq, void *base_addr, int size,
+ enum smq_type type, struct mutex *lock_ptr)
+{
+ int num_blocks;
+ uint8_t *pb_orig;
+ uint8_t *pb;
+ uint32_t i;
+ int err;
+
+ if (SMQ_MAGIC_INIT == smq->initialized) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ if (!base_addr || !size) {
+ err = SMQ_EBADPARM;
+ goto bail;
+ }
+
+ if (type == PRODUCER)
+ smq->lock = lock_ptr;
+
+ pb_orig = (uint8_t *)base_addr;
+ smq->hdr = (struct smq_hdr *)pb_orig;
+ pb = pb_orig;
+ pb += sizeof(struct smq_hdr);
+ pb = PTR_ALIGN(pb, 8);
+ size -= pb - (uint8_t *)pb_orig;
+ num_blocks = (int)((size - sizeof(struct smq_out_state) -
+ sizeof(struct smq_in_state))/(SM_BLOCKSIZE +
+ sizeof(struct smq_node) * 2));
+ if (0 >= num_blocks) {
+ err = SMQ_ENOMEMORY;
+ goto bail;
+ }
+
+ smq->blocks = pb;
+ smq->num_blocks = num_blocks;
+ pb += num_blocks * SM_BLOCKSIZE;
+ smq->out = (struct smq_out *)pb;
+ pb += sizeof(struct smq_out_state) + (num_blocks *
+ sizeof(struct smq_node));
+ smq->in = (struct smq_in *)pb;
+ smq->type = type;
+ if (PRODUCER == type) {
+ smq->hdr->producer_version = SM_VERSION;
+ for (i = 0; i < smq->num_blocks; i++)
+ (smq->out->sent + i)->index_block = 0xFFFF;
+
+ err = smq_blockmap_ctor(&smq->block_map, smq->num_blocks);
+ if (SMQ_SUCCESS != err)
+ goto bail;
+
+ smq->out->s.index_sent_write = 0;
+ smq->out->s.index_free_read = 0;
+ if (smq->out->s.init == SMQ_MAGIC_PRODUCER) {
+ smq->out->s.index_check_queue_for_reset += 1;
+ } else {
+ smq->out->s.index_check_queue_for_reset = 1;
+ smq->out->s.init = SMQ_MAGIC_PRODUCER;
+ }
+ } else {
+ smq->hdr->consumer_version = SM_VERSION;
+ for (i = 0; i < smq->num_blocks; i++)
+ (smq->in->free + i)->index_block = 0xFFFF;
+
+ smq->in->s.index_sent_read = 0;
+ smq->in->s.index_free_write = 0;
+ if (smq->out->s.init == SMQ_MAGIC_PRODUCER) {
+ smq->in->s.index_check_queue_for_reset_ack =
+ smq->out->s.index_check_queue_for_reset;
+ } else {
+ smq->in->s.index_check_queue_for_reset_ack = 0;
+ }
+
+ smq->in->s.init = SMQ_MAGIC_CONSUMER;
+ }
+ smq->initialized = SMQ_MAGIC_INIT;
+ err = SMQ_SUCCESS;
+
+bail:
+ return err;
+}
+
+static void send_interrupt_to_subsystem(struct rdbg_data *rdbgdata)
+{
+ int offset = rdbgdata->gpio_out_offset;
+ int val = 1 ^ gpio_get_value(rdbgdata->out.gpio_base_id + offset);
+ gpio_set_value(rdbgdata->out.gpio_base_id + offset, val);
+ rdbgdata->gpio_out_offset = (offset + 1) % 32;
+
+ dev_dbg(rdbgdata->device, "%s: sent interrupt %d to subsystem",
+ __func__, val);
+}
+
+static irqreturn_t on_interrupt_from(int irq, void *ptr)
+{
+ struct rdbg_data *rdbgdata = (struct rdbg_data *) ptr;
+
+ dev_dbg(rdbgdata->device, "%s: Received interrupt %d from subsystem",
+ __func__, irq);
+
+ complete(&(rdbgdata->work));
+ return IRQ_HANDLED;
+}
+
+static int initialize_smq(struct rdbg_data *rdbgdata)
+{
+ int err = 0;
+
+ if (smq_ctor(&(rdbgdata->producer_smrb), (void *)(rdbgdata->smem_addr),
+ ((rdbgdata->smem_size)/2), PRODUCER, &rdbgdata->write_mutex)) {
+ dev_err(rdbgdata->device, "%s: smq producer allocation failed",
+ __func__);
+ err = -ENOMEM;
+ goto bail;
+ }
+
+ if (smq_ctor(&(rdbgdata->consumer_smrb), (void *)((uint32_t)
+ (rdbgdata->smem_addr) + ((rdbgdata->smem_size)/2)),
+ ((rdbgdata->smem_size)/2), CONSUMER, NULL)) {
+ dev_err(rdbgdata->device, "%s: smq conmsumer allocation failed",
+ __func__);
+ err = -ENOMEM;
+ }
+
+bail:
+ return err;
+
+}
+
+static int rdbg_open(struct inode *inode, struct file *filp)
+{
+ int device_id = -1;
+ struct rdbg_device *device = &g_rdbg_instance;
+ struct rdbg_data *rdbgdata = NULL;
+ int err = 0;
+
+ if (!inode || !device->rdbg_data) {
+ pr_err("Memory not allocated yet");
+ err = -ENODEV;
+ goto bail;
+ }
+
+ device_id = MINOR(inode->i_rdev);
+ rdbgdata = &device->rdbg_data[device_id];
+
+ if (rdbgdata->device_opened) {
+ dev_err(rdbgdata->device, "%s: Device already opened",
+ __func__);
+ err = -EEXIST;
+ goto bail;
+ }
+
+ rdbgdata->smem_size = proc_info[device_id].smem_buffer_size;
+ if (!rdbgdata->smem_size) {
+ dev_err(rdbgdata->device, "%s: smem not initialized", __func__);
+ err = -ENOMEM;
+ goto bail;
+ }
+
+ rdbgdata->smem_addr = smem_alloc(proc_info[device_id].smem_buffer_addr,
+ rdbgdata->smem_size);
+ if (!rdbgdata->smem_addr) {
+ dev_err(rdbgdata->device, "%s: Could not allocate smem memory",
+ __func__);
+ err = -ENOMEM;
+ goto bail;
+ }
+ dev_dbg(rdbgdata->device, "%s: SMEM address=0x%x smem_size=%d",
+ __func__, (unsigned int)rdbgdata->smem_addr,
+ rdbgdata->smem_size);
+
+ if (check_subsystem_debug_enabled(rdbgdata->smem_addr,
+ rdbgdata->smem_size/2)) {
+ dev_err(rdbgdata->device, "%s: Subsystem %s is not debug enabled",
+ __func__, proc_info[device_id].name);
+ err = -ECOMM;
+ goto bail;
+ }
+
+ init_completion(&rdbgdata->work);
+
+ err = request_irq(rdbgdata->in.irq_base_id, on_interrupt_from,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ proc_info[device_id].name,
+ (void *)&device->rdbg_data[device_id]);
+ if (err) {
+ dev_err(rdbgdata->device,
+ "%s: Failed to register interrupt.Err=%d,irqid=%d.",
+ __func__, err, rdbgdata->in.irq_base_id);
+ goto irq_bail;
+ }
+
+ err = enable_irq_wake(rdbgdata->in.irq_base_id);
+ if (err < 0) {
+ dev_dbg(rdbgdata->device, "enable_irq_wake() failed with err=%d",
+ err);
+ err = 0;
+ }
+
+ mutex_init(&rdbgdata->write_mutex);
+
+ err = initialize_smq(rdbgdata);
+ if (err) {
+ dev_err(rdbgdata->device, "Error initializing smq. Err=%d",
+ err);
+ goto smq_bail;
+ }
+
+ rdbgdata->device_opened = 1;
+
+ filp->private_data = (void *)rdbgdata;
+
+ return 0;
+
+smq_bail:
+ smq_dtor(&(rdbgdata->producer_smrb));
+ smq_dtor(&(rdbgdata->consumer_smrb));
+ mutex_destroy(&rdbgdata->write_mutex);
+irq_bail:
+ free_irq(rdbgdata->in.irq_base_id, (void *)
+ &device->rdbg_data[device_id]);
+bail:
+ return err;
+}
+
+static int rdbg_release(struct inode *inode, struct file *filp)
+{
+ int device_id = -1;
+ struct rdbg_device *rdbgdevice = &g_rdbg_instance;
+ struct rdbg_data *rdbgdata = NULL;
+ int err = 0;
+
+ if (!inode || !rdbgdevice->rdbg_data) {
+ pr_err("Memory not allocated yet");
+ err = -ENODEV;
+ goto bail;
+ }
+
+ device_id = MINOR(inode->i_rdev);
+ rdbgdata = &rdbgdevice->rdbg_data[device_id];
+
+ if (rdbgdata->device_opened == 1) {
+ dev_dbg(rdbgdata->device, "%s: Destroying %s.", __func__,
+ proc_info[device_id].name);
+ rdbgdata->device_opened = 0;
+ complete(&(rdbgdata->work));
+ free_irq(rdbgdata->in.irq_base_id, (void *)
+ &rdbgdevice->rdbg_data[device_id]);
+ if (rdbgdevice->rdbg_data[device_id].producer_smrb.initialized)
+ smq_dtor(&(rdbgdevice->rdbg_data[device_id].
+ producer_smrb));
+ if (rdbgdevice->rdbg_data[device_id].consumer_smrb.initialized)
+ smq_dtor(&(rdbgdevice->rdbg_data[device_id].
+ consumer_smrb));
+ mutex_destroy(&rdbgdata->write_mutex);
+ }
+
+ filp->private_data = NULL;
+
+bail:
+ return err;
+}
+
+static ssize_t rdbg_read(struct file *filp, char __user *buf, size_t size,
+ loff_t *offset)
+{
+ int err = 0;
+ struct rdbg_data *rdbgdata = filp->private_data;
+ void *p_sent_buffer = NULL;
+ int nsize = 0;
+ int more = 0;
+
+ if (!rdbgdata) {
+ pr_err("Invalid argument");
+ err = -EINVAL;
+ goto bail;
+ }
+
+ dev_dbg(rdbgdata->device, "%s: In receive", __func__);
+ err = wait_for_completion_interruptible(&(rdbgdata->work));
+ if (err) {
+ dev_err(rdbgdata->device, "%s: Error in wait", __func__);
+ goto bail;
+ }
+
+ smq_check_queue_reset(&(rdbgdata->consumer_smrb),
+ &(rdbgdata->producer_smrb));
+ if (SMQ_SUCCESS != smq_receive(&(rdbgdata->consumer_smrb),
+ &p_sent_buffer, &nsize, &more)) {
+ dev_err(rdbgdata->device, "%s: Error in smq_recv(). Err code = %d",
+ __func__, err);
+ err = -ENODATA;
+ goto bail;
+ }
+
+ size = ((size < nsize) ? size : nsize);
+ err = copy_to_user(buf, p_sent_buffer, size);
+ if (err != 0) {
+ dev_err(rdbgdata->device, "%s: Error in copy_to_user(). Err code = %d",
+ __func__, err);
+ err = -ENODATA;
+ goto bail;
+ }
+
+ smq_free(&(rdbgdata->consumer_smrb), p_sent_buffer);
+ err = size;
+ dev_dbg(rdbgdata->device, "%s: Read data to buffer with address 0x%x",
+ __func__, (unsigned int) buf);
+
+bail:
+ dev_dbg(rdbgdata->device, "%s: Returning from receive", __func__);
+ return err;
+}
+
+static ssize_t rdbg_write(struct file *filp, const char __user *buf,
+ size_t size, loff_t *offset)
+{
+ int err = 0;
+ struct rdbg_data *rdbgdata = filp->private_data;
+
+ if (!rdbgdata) {
+ pr_err("Invalid argument");
+ err = -EINVAL;
+ goto bail;
+ }
+
+ if (smq_alloc_send(&(rdbgdata->producer_smrb), buf, size)) {
+ dev_err(rdbgdata->device, "%s, Error sending", __func__);
+ err = -ECOMM;
+ goto bail;
+ }
+ send_interrupt_to_subsystem(rdbgdata);
+
+ err = size;
+
+bail:
+ return err;
+}
+
+
+static const struct file_operations rdbg_fops = {
+ .open = rdbg_open,
+ .read = rdbg_read,
+ .write = rdbg_write,
+ .release = rdbg_release,
+};
+
+static int register_smp2p(char *node_name, struct gpio_info *gpio_info_ptr)
+{
+ struct device_node *node = NULL;
+ int cnt = 0;
+ int id = 0;
+
+ node = of_find_compatible_node(NULL, NULL, node_name);
+ if (node) {
+ cnt = of_gpio_count(node);
+ if (cnt && gpio_info_ptr) {
+ id = of_get_gpio(node, 0);
+ gpio_info_ptr->gpio_base_id = id;
+ gpio_info_ptr->irq_base_id = gpio_to_irq(id);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int __init rdbg_init(void)
+{
+ int err = 0;
+ struct rdbg_device *rdbgdevice = &g_rdbg_instance;
+ int minor = 0;
+ int major = 0;
+ int minor_nodes_created = 0;
+
+ char *rdbg_compatible_string = "qcom,smp2pgpio_client_rdbg_";
+ int max_len = strlen(rdbg_compatible_string) + strlen("xx_out");
+
+ char *node_name = kcalloc(max_len, sizeof(char), GFP_KERNEL);
+
+ if (!node_name) {
+ pr_err("Not enough memory");
+ err = -ENOMEM;
+ goto bail;
+ }
+
+ if (rdbgdevice->num_devices < 1 ||
+ rdbgdevice->num_devices > SMP2P_NUM_PROCS) {
+ pr_err("rgdb: invalid num_devices");
+ err = -EDOM;
+ goto name_bail;
+ }
+
+ rdbgdevice->rdbg_data = kcalloc(rdbgdevice->num_devices,
+ sizeof(struct rdbg_data), GFP_KERNEL);
+ if (!rdbgdevice->rdbg_data) {
+ pr_err("Not enough memory for rdbg devices");
+ err = -ENOMEM;
+ goto name_bail;
+ }
+
+ err = alloc_chrdev_region(&rdbgdevice->dev_no, 0,
+ rdbgdevice->num_devices, "rdbgctl");
+ if (err) {
+ pr_err("Error in alloc_chrdev_region.");
+ goto data_bail;
+ }
+ major = MAJOR(rdbgdevice->dev_no);
+
+ cdev_init(&rdbgdevice->cdev, &rdbg_fops);
+ rdbgdevice->cdev.owner = THIS_MODULE;
+ err = cdev_add(&rdbgdevice->cdev, MKDEV(major, 0),
+ rdbgdevice->num_devices);
+ if (err) {
+ pr_err("Error in cdev_add");
+ goto chrdev_bail;
+ }
+
+ rdbgdevice->class = class_create(THIS_MODULE, "rdbg");
+ if (IS_ERR(rdbgdevice->class)) {
+ err = PTR_ERR(rdbgdevice->class);
+ pr_err("Error in class_create");
+ goto cdev_bail;
+ }
+
+ for (minor = 0; minor < rdbgdevice->num_devices; minor++) {
+ if (!proc_info[minor].name)
+ continue;
+
+ if (snprintf(node_name, max_len, "%s%d_in",
+ rdbg_compatible_string, minor) <= 0) {
+ pr_err("Error in snprintf");
+ err = -ENOMEM;
+ goto device_bail;
+ }
+
+ if (register_smp2p(node_name,
+ &rdbgdevice->rdbg_data[minor].in)) {
+ pr_debug("No incoming device tree entry found for %s",
+ proc_info[minor].name);
+ continue;
+ }
+
+ if (snprintf(node_name, max_len, "%s%d_out",
+ rdbg_compatible_string, minor) <= 0) {
+ pr_err("Error in snprintf");
+ err = -ENOMEM;
+ goto device_bail;
+ }
+
+ if (register_smp2p(node_name,
+ &rdbgdevice->rdbg_data[minor].out)) {
+ pr_err("No outgoing device tree entry found for %s",
+ proc_info[minor].name);
+ err = -EINVAL;
+ goto device_bail;
+ }
+
+ rdbgdevice->rdbg_data[minor].device = device_create(
+ rdbgdevice->class, NULL, MKDEV(major, minor),
+ NULL, "%s", proc_info[minor].name);
+ if (IS_ERR(rdbgdevice->rdbg_data[minor].device)) {
+ err = PTR_ERR(rdbgdevice->rdbg_data[minor].device);
+ pr_err("Error in device_create");
+ goto device_bail;
+ }
+ rdbgdevice->rdbg_data[minor].device_initialized = 1;
+ minor_nodes_created++;
+ dev_dbg(rdbgdevice->rdbg_data[minor].device,
+ "%s: created /dev/%s c %d %d'", __func__,
+ proc_info[minor].name, major, minor);
+ }
+
+ if (!minor_nodes_created) {
+ pr_err("No device tree entries found");
+ err = -EINVAL;
+ goto class_bail;
+ }
+
+ goto name_bail;
+
+device_bail:
+ for (--minor; minor >= 0; minor--) {
+ if (rdbgdevice->rdbg_data[minor].device_initialized)
+ device_destroy(rdbgdevice->class,
+ MKDEV(MAJOR(rdbgdevice->dev_no), minor));
+ }
+class_bail:
+ class_destroy(rdbgdevice->class);
+cdev_bail:
+ cdev_del(&rdbgdevice->cdev);
+chrdev_bail:
+ unregister_chrdev_region(rdbgdevice->dev_no, rdbgdevice->num_devices);
+data_bail:
+ kfree(rdbgdevice->rdbg_data);
+name_bail:
+ kfree(node_name);
+bail:
+ return err;
+}
+
+static void __exit rdbg_exit(void)
+{
+ struct rdbg_device *rdbgdevice = &g_rdbg_instance;
+ int minor;
+
+ for (minor = 0; minor < rdbgdevice->num_devices; minor++) {
+ if (rdbgdevice->rdbg_data[minor].device_initialized) {
+ device_destroy(rdbgdevice->class,
+ MKDEV(MAJOR(rdbgdevice->dev_no), minor));
+ }
+ }
+ class_destroy(rdbgdevice->class);
+ cdev_del(&rdbgdevice->cdev);
+ unregister_chrdev_region(rdbgdevice->dev_no, 1);
+ kfree(rdbgdevice->rdbg_data);
+}
+
+module_init(rdbg_init);
+module_exit(rdbg_exit);
+
+MODULE_DESCRIPTION("rdbg module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 8921c61..b773e1b 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1574,9 +1574,11 @@
* Keys that have been pressed at suspend time are unlikely
* to be still pressed when we resume.
*/
- spin_lock_irq(&dev->event_lock);
- input_dev_release_keys(dev);
- spin_unlock_irq(&dev->event_lock);
+ if (!test_bit(INPUT_PROP_NO_DUMMY_RELEASE, dev->propbit)) {
+ spin_lock_irq(&dev->event_lock);
+ input_dev_release_keys(dev);
+ spin_unlock_irq(&dev->event_lock);
+ }
}
mutex_unlock(&dev->mutex);
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index b011315..8123920 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -67,10 +67,10 @@
#define F11_STD_CTRL_LEN 10
#define F11_STD_DATA_LEN 12
-#define NORMAL_OPERATION (0 << 0)
-#define SENSOR_SLEEP (1 << 0)
-#define NO_SLEEP_OFF (0 << 2)
-#define NO_SLEEP_ON (1 << 2)
+#define NORMAL_OPERATION 0
+#define SENSOR_SLEEP 1
+#define NO_SLEEP_OFF 0
+#define NO_SLEEP_ON 1
enum device_status {
STATUS_NO_ERROR = 0x00,
@@ -111,6 +111,13 @@
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data);
+static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data);
+
+static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data);
+
+static int synaptics_rmi4_check_configuration(struct synaptics_rmi4_data
+ *rmi4_data);
+
#ifdef CONFIG_PM
static int synaptics_rmi4_suspend(struct device *dev);
@@ -2051,6 +2058,30 @@
return 0;
}
+/*
+* This function checks whether the fhandler already existis in the
+* support_fn_list or not.
+* If it exists then return 1 as found or return 0 as not found.
+*
+* Called by synaptics_rmi4_query_device().
+*/
+static int synaptics_rmi4_check_fn_list(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int found = 0;
+ struct synaptics_rmi4_fn *new_fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ if (!list_empty(&rmi->support_fn_list))
+ list_for_each_entry(new_fhandler, &rmi->support_fn_list, link)
+ if (new_fhandler->fn_number == fhandler->fn_number)
+ found = 1;
+
+ return found;
+}
+
/**
* synaptics_rmi4_query_device()
*
@@ -2066,7 +2097,7 @@
*/
static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
{
- int retval;
+ int retval, found;
unsigned char ii;
unsigned char page_number;
unsigned char intr_count = 0;
@@ -2080,8 +2111,6 @@
rmi = &(rmi4_data->rmi4_mod_info);
- INIT_LIST_HEAD(&rmi->support_fn_list);
-
/* Scan the page description tables of the pages to service */
for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END;
@@ -2096,7 +2125,7 @@
return retval;
fhandler = NULL;
-
+ found = 0;
if (rmi_fd.fn_number == 0) {
dev_dbg(&rmi4_data->i2c_client->dev,
"%s: Reached end of PDT\n",
@@ -2215,8 +2244,28 @@
intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
if (fhandler && rmi_fd.intr_src_count) {
- list_add_tail(&fhandler->link,
+ /* Want to check whether the fhandler already
+ exists in the support_fn_list or not.
+ If not found then add it to the list, otherwise
+ free the memory allocated to it.
+ */
+ found = synaptics_rmi4_check_fn_list(rmi4_data,
+ fhandler);
+
+ if (!found) {
+ list_add_tail(&fhandler->link,
&rmi->support_fn_list);
+ } else {
+ if (fhandler->fn_number ==
+ SYNAPTICS_RMI4_F1A) {
+ synaptics_rmi4_f1a_kfree(
+ fhandler);
+ } else {
+ kfree(fhandler->data);
+ kfree(fhandler->extra);
+ }
+ kfree(fhandler);
+ }
}
}
}
@@ -2851,6 +2900,8 @@
init_waitqueue_head(&rmi4_data->wait);
mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
+ INIT_LIST_HEAD(&rmi->support_fn_list);
+
retval = synaptics_rmi4_query_device(rmi4_data);
if (retval < 0) {
dev_err(&client->dev,
@@ -2987,6 +3038,9 @@
goto err_sysfs;
}
}
+
+ synaptics_rmi4_sensor_wake(rmi4_data);
+
retval = synaptics_rmi4_irq_enable(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev,
@@ -2995,6 +3049,12 @@
goto err_sysfs;
}
+ retval = synaptics_rmi4_check_configuration(rmi4_data);
+ if (retval < 0) {
+ dev_err(&client->dev, "Failed to check configuration\n");
+ return retval;
+ }
+
return retval;
err_sysfs:
@@ -3175,6 +3235,12 @@
return;
}
+ if (device_ctrl.nosleep == NO_SLEEP_OFF &&
+ device_ctrl.sleep_mode == NORMAL_OPERATION) {
+ rmi4_data->sensor_sleep = false;
+ return;
+ }
+
device_ctrl.sleep_mode = NORMAL_OPERATION;
device_ctrl.nosleep = NO_SLEEP_OFF;
@@ -3558,6 +3624,22 @@
static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
};
#endif
+#else
+static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+};
+
+static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+};
+
+static int synaptics_rmi4_check_configuration(struct synaptics_rmi4_data
+ *rmi4_data)
+{
+ return 0;
+};
#endif
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 9d23d12..eeae873 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -528,7 +528,7 @@
u32 ctrl_status = 0, count = 0, rc = 0;
int max_tries = 100;
venus_hfi_write_register(device,
- VIDC_WRAPPER_INTR_MASK, 0x8, 0);
+ VIDC_WRAPPER_INTR_MASK, VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK, 0);
venus_hfi_write_register(device,
VIDC_CPU_CS_SCIACMDARG3, 1, 0);
@@ -1061,6 +1061,8 @@
dprintk(VIDC_ERR, "Failed venus clock enable");
goto fail_clk_power_on;
}
+ venus_hfi_write_register(device,
+ VIDC_WRAPPER_INTR_MASK, VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK, 0);
}
already_enabled:
device->clocks_enabled = 1;
@@ -1662,6 +1664,11 @@
dprintk(VIDC_DBG, "Clocks are already disabled");
goto already_disabled;
}
+ /*SYS Idle should be last message so mask any further interrupts
+ * until clocks are enabled again.*/
+ venus_hfi_write_register(device,
+ VIDC_WRAPPER_INTR_MASK,
+ VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK | VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK, 0);
venus_hfi_clk_disable(device);
if (!queue_delayed_work(device->venus_pm_workq, &venus_hfi_pm_work,
msecs_to_jiffies(msm_vidc_pwr_collapse_delay)))
@@ -1673,7 +1680,7 @@
static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device)
{
u32 intr_status = 0;
- int rc = 0, ctrl_status;
+ int rc = 0;
if (!device->callback)
return;
@@ -1707,19 +1714,6 @@
VIDC_CPU_CS_A2HSOFTINTCLR, 1, 0);
venus_hfi_write_register(device,
VIDC_WRAPPER_INTR_CLEAR, intr_status, 0);
- rc = venus_hfi_is_cmd_pending(device);
- ctrl_status = venus_hfi_read_register(
- device,
- VIDC_CPU_CS_SCIACMDARG0);
- dprintk(VIDC_INFO,
- "CLEAR INTERRUPT - cmd_pending %d, ctrl_status 0x%x\n",
- rc, ctrl_status);
- if ((ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK)
- && !rc) {
- dprintk(VIDC_DBG, "SYS_IDLE interrupt, disable clocks\n");
- venus_hfi_clk_gating_on(device);
- }
-
dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt");
err_clk_gating_off:
mutex_unlock(&device->clk_pwr_lock);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
index 90163b9..8ec0e28 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_io.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
@@ -88,8 +88,10 @@
#define VIDC_WRAPPER_INTR_MASK (VIDC_WRAPPER_BASE_OFFS + 0x10)
#define VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK 0x10
#define VIDC_WRAPPER_INTR_MASK_A2HWD_SHFT 0x4
-#define VIDC_WRAPPER_INTR_MASK_A2H_BMSK 0x4
-#define VIDC_WRAPPER_INTR_MASK_A2H_SHFT 0x2
+#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK 0x8
+#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_SHFT 0x3
+#define VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK 0x4
+#define VIDC_WRAPPER_INTR_MASK_A2HCPU_SHFT 0x2
#define VIDC_WRAPPER_INTR_CLEAR (VIDC_WRAPPER_BASE_OFFS + 0x14)
#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK 0x10
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 50d5f7b..b55b66b 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -705,6 +705,8 @@
pon->pon_input->phys = "qpnp_pon/input0";
}
+ /* don't send dummy release event when system resumes */
+ __set_bit(INPUT_PROP_NO_DUMMY_RELEASE, pon->pon_input->propbit);
input_set_capability(pon->pon_input, EV_KEY, cfg->key_code);
return 0;
@@ -1116,8 +1118,8 @@
}
static struct of_device_id spmi_match_table[] = {
- { .compatible = "qcom,qpnp-power-on",
- }
+ { .compatible = "qcom,qpnp-power-on", },
+ {}
};
static struct spmi_driver qpnp_pon_driver = {
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
index 042491d..bb27e6b 100644
--- a/drivers/video/msm/mdss/mdss_edp.c
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -228,38 +228,273 @@
}
}
-void mdss_edp_config_sync(unsigned char *base)
+int mdss_edp_mainlink_ready(struct mdss_edp_drv_pdata *ep, u32 which)
+{
+ u32 data;
+ int cnt = 10;
+
+ while (--cnt) {
+ data = edp_read(ep->base + 0x84); /* EDP_MAINLINK_READY */
+ if (data & which) {
+ pr_debug("%s: which=%x ready\n", __func__, which);
+ return 1;
+ }
+ usleep(1000);
+ }
+ pr_err("%s: which=%x NOT ready\n", __func__, which);
+
+ return 0;
+}
+
+void mdss_edp_mainlink_reset(struct mdss_edp_drv_pdata *ep)
+{
+ edp_write(ep->base + 0x04, 0x02); /* EDP_MAINLINK_CTRL */
+ usleep(1000);
+ edp_write(ep->base + 0x04, 0); /* EDP_MAINLINK_CTRL */
+}
+
+void mdss_edp_mainlink_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
+{
+ u32 data;
+
+ data = edp_read(ep->base + 0x04);
+ data &= ~BIT(0);
+
+ if (enable)
+ data |= 0x1;
+
+ edp_write(ep->base + 0x04, data);
+}
+
+void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state)
+{
+ edp_write(ep->base + EDP_STATE_CTRL, state);
+}
+
+void mdss_edp_aux_reset(struct mdss_edp_drv_pdata *ep)
+{
+ /* reset AUX */
+ edp_write(ep->base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
+ usleep(1000);
+ edp_write(ep->base + 0x300, 0); /* EDP_AUX_CTRL */
+}
+
+void mdss_edp_aux_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
+{
+ u32 data;
+
+ data = edp_read(ep->base + 0x300);
+ if (enable)
+ data |= 0x01;
+ else
+ data |= ~0x01;
+ edp_write(ep->base + 0x300, data); /* EDP_AUX_CTRL */
+}
+
+void mdss_edp_phy_pll_reset(struct mdss_edp_drv_pdata *ep)
+{
+ /* EDP_PHY_CTRL */
+ edp_write(ep->base + 0x74, 0x005); /* bit 0, 2 */
+ usleep(1000);
+ edp_write(ep->base + 0x74, 0x000); /* EDP_PHY_CTRL */
+}
+
+int mdss_edp_phy_pll_ready(struct mdss_edp_drv_pdata *ep)
+{
+ int cnt;
+ u32 status = 0;
+
+ cnt = 100;
+ while (--cnt) {
+ status = edp_read(ep->base + 0x6c0);
+ if (status & 0x01)
+ break;
+ usleep(100);
+ }
+
+ pr_debug("%s: PLL cnt=%d status=%x\n", __func__, cnt, (int)status);
+
+ if (cnt <= 0) {
+ pr_err("%s: PLL NOT ready\n", __func__);
+ return 0;
+ } else
+ return 1;
+}
+
+int mdss_edp_phy_ready(struct mdss_edp_drv_pdata *ep)
+{
+ u32 status;
+
+ status = edp_read(ep->base + 0x598);
+ status &= 0x01;
+
+ return status;
+}
+
+void mdss_edp_phy_power_ctrl(struct mdss_edp_drv_pdata *ep, int enable)
+{
+ if (enable) {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(ep->base + 0x52c, 0x3f);
+ /* EDP_PHY_EDPPHY_GLB_CFG */
+ edp_write(ep->base + 0x528, 0x1);
+ /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
+ edp_write(ep->base + 0x620, 0xf);
+ } else {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(ep->base + 0x52c, 0xc0);
+ }
+}
+
+void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up)
+{
+ int i, off, max_lane;
+ u32 data;
+
+ max_lane = ep->lane_cnt;
+
+ if (up)
+ data = 0; /* power up */
+ else
+ data = 0x7; /* power down */
+
+ /* EDP_PHY_EDPPHY_LNn_PD_CTL */
+ for (i = 0; i < max_lane; i++) {
+ off = 0x40 * i;
+ edp_write(ep->base + 0x404 + off , data);
+ }
+
+ /* power down un used lane */
+ data = 0x7; /* power down */
+ for (i = max_lane; i < EDP_MAX_LANE; i++) {
+ off = 0x40 * i;
+ edp_write(ep->base + 0x404 + off , data);
+ }
+}
+
+void mdss_edp_clock_synchrous(struct mdss_edp_drv_pdata *ep, int sync)
+{
+ u32 data;
+
+ /* EDP_MISC1_MISC0 */
+ data = edp_read(ep->base + 0x02c);
+
+ if (sync)
+ data |= 0x01;
+ else
+ data &= ~0x01;
+
+ /* EDP_MISC1_MISC0 */
+ edp_write(ep->base + 0x2c, data);
+}
+
+/* voltage mode and pre emphasis cfg */
+void mdss_edp_phy_vm_pe_init(struct mdss_edp_drv_pdata *ep)
+{
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
+ edp_write(ep->base + 0x510, 0x3); /* vm only */
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
+ edp_write(ep->base + 0x514, 0x64);
+ /* EDP_PHY_EDPPHY_GLB_MISC9 */
+ edp_write(ep->base + 0x518, 0x6c);
+}
+
+void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep)
+{
+ struct dpcd_cap *cap;
+ struct display_timing_desc *dp;
+ u32 data = 0;
+
+ dp = &ep->edid.timing[0];
+
+ cap = &ep->dpcd;
+
+ data = ep->lane_cnt - 1;
+ data <<= 4;
+
+ if (cap->enhanced_frame)
+ data |= 0x40;
+
+ if (ep->edid.color_depth == 8) {
+ /* 0 == 6 bits, 1 == 8 bits */
+ data |= 0x100; /* bit 8 */
+ }
+
+ if (!dp->interlaced) /* progressive */
+ data |= 0x04;
+
+ data |= 0x03; /* sycn clock & static Mvid */
+
+ edp_write(ep->base + 0xc, data); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_sw_mvid_nvid(struct mdss_edp_drv_pdata *ep)
+{
+ edp_write(ep->base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
+ edp_write(ep->base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
+}
+
+static void mdss_edp_timing_cfg(struct mdss_edp_drv_pdata *ep)
+{
+ struct mdss_panel_info *pinfo;
+ u32 total_ver, total_hor;
+ u32 data;
+
+ pinfo = &ep->panel_data.panel_info;
+
+ pr_debug("%s: width=%d hporch= %d %d %d\n", __func__,
+ pinfo->xres, pinfo->lcdc.h_back_porch,
+ pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width);
+
+ pr_debug("%s: height=%d vporch= %d %d %d\n", __func__,
+ pinfo->yres, pinfo->lcdc.v_back_porch,
+ pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width);
+
+ total_hor = pinfo->xres + pinfo->lcdc.h_back_porch +
+ pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width;
+
+ total_ver = pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width;
+
+ data = total_ver;
+ data <<= 16;
+ data |= total_hor;
+ edp_write(ep->base + 0x1c, data); /* EDP_TOTAL_HOR_VER */
+
+ data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width);
+ data <<= 16;
+ data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width);
+ edp_write(ep->base + 0x20, data); /* EDP_START_HOR_VER_FROM_SYNC */
+
+ data = pinfo->lcdc.v_pulse_width;
+ data <<= 16;
+ data |= pinfo->lcdc.h_pulse_width;
+ edp_write(ep->base + 0x24, data); /* EDP_HSYNC_VSYNC_WIDTH_POLARITY */
+
+ data = pinfo->yres;
+ data <<= 16;
+ data |= pinfo->xres;
+ edp_write(ep->base + 0x28, data); /* EDP_ACTIVE_HOR_VER */
+}
+
+int mdss_edp_wait4train(struct mdss_edp_drv_pdata *edp_drv)
{
int ret = 0;
- ret = edp_read(base + 0xc); /* EDP_CONFIGURATION_CTRL */
- ret &= ~0x733;
- ret |= (0x55 & 0x733);
- edp_write(base + 0xc, ret);
- edp_write(base + 0xc, 0x55); /* EDP_CONFIGURATION_CTRL */
-}
+ if (edp_drv->cont_splash)
+ return ret;
-static void mdss_edp_config_sw_div(unsigned char *base)
-{
- edp_write(base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
- edp_write(base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
-}
+ ret = wait_for_completion_timeout(&edp_drv->video_comp, 30);
+ if (ret <= 0) {
+ pr_err("%s: Link Train timedout\n", __func__);
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
-static void mdss_edp_config_static_mdiv(unsigned char *base)
-{
- int ret = 0;
+ pr_debug("%s:\n", __func__);
- ret = edp_read(base + 0xc); /* EDP_CONFIGURATION_CTRL */
- edp_write(base + 0xc, ret | 0x2); /* EDP_CONFIGURATION_CTRL */
- edp_write(base + 0xc, 0x57); /* EDP_CONFIGURATION_CTRL */
-}
-
-static void mdss_edp_enable(unsigned char *base, int enable)
-{
- edp_write(base + 0x8, 0x0); /* EDP_STATE_CTRL */
- edp_write(base + 0x8, 0x40); /* EDP_STATE_CTRL */
- edp_write(base + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
- edp_write(base + 0x4, enable); /* EDP_MAINLINK_CTRL */
+ return ret;
}
static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv);
@@ -278,73 +513,53 @@
edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
panel_data);
- pr_debug("%s:+\n", __func__);
- if (edp_drv->train_start == 0)
- edp_drv->train_start++;
+ pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash);
- mdss_edp_phy_pll_reset(edp_drv->base);
- mdss_edp_aux_reset(edp_drv->base);
- mdss_edp_mainlink_reset(edp_drv->base);
-
- ret = mdss_edp_prepare_clocks(edp_drv);
- if (ret)
- return ret;
- mdss_edp_phy_powerup(edp_drv->base, 1);
-
- mdss_edp_pll_configure(edp_drv->base, edp_drv->edid.timing[0].pclk);
- mdss_edp_phy_pll_ready(edp_drv->base);
-
- ret = mdss_edp_clk_enable(edp_drv);
- if (ret) {
- mdss_edp_unprepare_clocks(edp_drv);
- return ret;
- }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- mdss_edp_aux_ctrl(edp_drv->base, 1);
+ if (!edp_drv->cont_splash) { /* vote for clocks */
+ mdss_edp_phy_pll_reset(edp_drv);
+ mdss_edp_aux_reset(edp_drv);
+ mdss_edp_mainlink_reset(edp_drv);
+ mdss_edp_aux_ctrl(edp_drv, 1);
- mdss_edp_lane_power_ctrl(edp_drv->base,
- edp_drv->dpcd.max_lane_count, 1);
- mdss_edp_enable_mainlink(edp_drv->base, 1);
- mdss_edp_config_clk(edp_drv->base, edp_drv->mmss_cc_base);
+ ret = mdss_edp_prepare_clocks(edp_drv);
+ if (ret)
+ return ret;
- mdss_edp_clock_synchrous(edp_drv->base, 1);
- mdss_edp_phy_vm_pe_init(edp_drv->base);
- mdss_edp_config_sync(edp_drv->base);
- mdss_edp_config_sw_div(edp_drv->base);
- mdss_edp_config_static_mdiv(edp_drv->base);
- gpio_set_value(edp_drv->gpio_panel_en, 1);
+ mdss_edp_phy_power_ctrl(edp_drv, 1);
+
+ ret = mdss_edp_clk_enable(edp_drv);
+ if (ret) {
+ mdss_edp_unprepare_clocks(edp_drv);
+ return ret;
+ }
+
+ mdss_edp_phy_pll_ready(edp_drv);
+
+ mdss_edp_lane_power_ctrl(edp_drv, 1);
+
+ mdss_edp_clock_synchrous(edp_drv, 1);
+ mdss_edp_phy_vm_pe_init(edp_drv);
+ mdss_edp_config_ctrl(edp_drv);
+ mdss_edp_sw_mvid_nvid(edp_drv);
+ mdss_edp_timing_cfg(edp_drv);
+
+ gpio_set_value(edp_drv->gpio_panel_en, 1);
+
+ INIT_COMPLETION(edp_drv->idle_comp);
+ mdss_edp_mainlink_ctrl(edp_drv, 1);
+ } else {
+ mdss_edp_aux_ctrl(edp_drv, 1);
+ }
+
mdss_edp_irq_enable(edp_drv);
+ mdss_edp_wait4train(edp_drv);
+
+ edp_drv->cont_splash = 0;
+
pr_debug("%s:-\n", __func__);
- return 0;
-}
-
-int mdss_edp_wait4train(struct mdss_panel_data *pdata)
-{
- struct mdss_edp_drv_pdata *edp_drv = NULL;
- int ret = 0;
-
- if (!pdata) {
- pr_err("%s: Invalid input data\n", __func__);
- return -EINVAL;
- }
-
- edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
- panel_data);
-
- ret = wait_for_completion_timeout(&edp_drv->train_comp, 100);
- if (ret <= 0) {
- pr_err("%s: Link Train timedout\n", __func__);
- ret = -EINVAL;
- } else {
- ret = 0;
- }
-
- mdss_edp_enable(edp_drv->base, 1);
-
- pr_debug("%s:\n", __func__);
-
return ret;
}
@@ -359,28 +574,44 @@
pr_err("%s: Invalid input data\n", __func__);
return -EINVAL;
}
- pr_debug("%s:+\n", __func__);
+ pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash);
+
+
+ INIT_COMPLETION(edp_drv->idle_comp);
+ mdss_edp_state_ctrl(edp_drv, ST_PUSH_IDLE);
+
+ ret = wait_for_completion_timeout(&edp_drv->idle_comp,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ pr_err("%s: idle pattern timedout\n", __func__);
+
+ mdss_edp_state_ctrl(edp_drv, 0);
+
+ mdss_edp_sink_power_state(edp_drv, SINK_POWER_OFF);
mdss_edp_irq_disable(edp_drv);
gpio_set_value(edp_drv->gpio_panel_en, 0);
+
if (edp_drv->bl_pwm != NULL)
pwm_disable(edp_drv->bl_pwm);
- mdss_edp_enable(edp_drv->base, 0);
- mdss_edp_unconfig_clk(edp_drv->base, edp_drv->mmss_cc_base);
- mdss_edp_enable_mainlink(edp_drv->base, 0);
- mdss_edp_lane_power_ctrl(edp_drv->base,
- edp_drv->dpcd.max_lane_count, 0);
+ mdss_edp_mainlink_reset(edp_drv);
+ mdss_edp_mainlink_ctrl(edp_drv, 0);
+
+ mdss_edp_lane_power_ctrl(edp_drv, 0);
+ mdss_edp_phy_power_ctrl(edp_drv, 0);
+
mdss_edp_clk_disable(edp_drv);
- mdss_edp_phy_powerup(edp_drv->base, 0);
mdss_edp_unprepare_clocks(edp_drv);
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- mdss_edp_aux_ctrl(edp_drv->base, 0);
+ mdss_edp_aux_ctrl(edp_drv, 0);
- pr_debug("%s:-\n", __func__);
- return ret;
+ pr_debug("%s-: state_ctrl=%x\n", __func__,
+ edp_read(edp_drv->base + 0x8));
+ return 0;
}
static int mdss_edp_event_handler(struct mdss_panel_data *pdata,
@@ -393,9 +624,6 @@
case MDSS_EVENT_UNBLANK:
rc = mdss_edp_on(pdata);
break;
- case MDSS_EVENT_PANEL_ON:
- rc = mdss_edp_wait4train(pdata);
- break;
case MDSS_EVENT_PANEL_OFF:
rc = mdss_edp_off(pdata);
break;
@@ -477,6 +705,9 @@
edp_drv->panel_data.event_handler = mdss_edp_event_handler;
edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
+ edp_drv->panel_data.panel_info.cont_splash_enabled =
+ edp_drv->cont_splash;
+
ret = mdss_register_panel(edp_drv->pdev, &edp_drv->panel_data);
if (ret) {
dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n");
@@ -539,10 +770,25 @@
return 0;
}
-static void mdss_edp_video_ready(struct mdss_edp_drv_pdata *edp_drv)
+static void mdss_edp_video_ready(struct mdss_edp_drv_pdata *ep)
{
pr_debug("%s: edp_video_ready\n", __func__);
+ complete(&ep->video_comp);
+}
+static void mdss_edp_idle_patterns_sent(struct mdss_edp_drv_pdata *ep)
+{
+ pr_debug("%s: idle_patterns_sent\n", __func__);
+ complete(&ep->idle_comp);
+}
+
+static void mdss_edp_do_link_train(struct mdss_edp_drv_pdata *ep)
+{
+ if (ep->cont_splash)
+ return;
+
+ INIT_COMPLETION(ep->train_comp);
+ mdss_edp_link_train(ep);
}
static int edp_event_thread(void *data)
@@ -579,13 +825,14 @@
if (todo & EV_DPCD_STATUS_READ)
mdss_edp_dpcd_status_read(ep);
- if (todo & EV_LINK_TRAIN) {
- INIT_COMPLETION(ep->train_comp);
- mdss_edp_link_train(ep);
- }
+ if (todo & EV_LINK_TRAIN)
+ mdss_edp_do_link_train(ep);
if (todo & EV_VIDEO_READY)
mdss_edp_video_ready(ep);
+
+ if (todo & EV_IDLE_PATTERNS_SENT)
+ mdss_edp_idle_patterns_sent(ep);
}
return 0;
@@ -607,11 +854,12 @@
u32 isr1, isr2, mask1, mask2;
u32 ack;
+ spin_lock(&ep->lock);
isr1 = edp_read(base + 0x308);
isr2 = edp_read(base + 0x30c);
- mask1 = isr1 & EDP_INTR_MASK1;
- mask2 = isr2 & EDP_INTR_MASK2;
+ mask1 = isr1 & ep->mask1;
+ mask2 = isr2 & ep->mask2;
isr1 &= ~mask1; /* remove masks bit */
isr2 &= ~mask2;
@@ -628,16 +876,19 @@
ack <<= 1; /* ack bits */
ack |= mask2;
edp_write(base + 0x30c, ack);
+ spin_unlock(&ep->lock);
if (isr1 & EDP_INTR_HPD) {
isr1 &= ~EDP_INTR_HPD; /* clear */
- if (ep->train_start)
- edp_send_events(ep, EV_LINK_TRAIN);
+ edp_send_events(ep, EV_LINK_TRAIN);
}
if (isr2 & EDP_INTR_READY_FOR_VIDEO)
edp_send_events(ep, EV_VIDEO_READY);
+ if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT)
+ edp_send_events(ep, EV_IDLE_PATTERNS_SENT);
+
if (isr1 && ep->aux_cmd_busy) {
/* clear EDP_AUX_TRANS_CTRL */
edp_write(base + 0x318, 0);
@@ -661,16 +912,24 @@
static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv)
{
- edp_write(edp_drv->base + 0x308, EDP_INTR_MASK1);
- edp_write(edp_drv->base + 0x30c, EDP_INTR_MASK2);
+ unsigned long flags;
+
+ spin_lock_irqsave(&edp_drv->lock, flags);
+ edp_write(edp_drv->base + 0x308, edp_drv->mask1);
+ edp_write(edp_drv->base + 0x30c, edp_drv->mask2);
+ spin_unlock_irqrestore(&edp_drv->lock, flags);
mdss_enable_irq(&mdss_edp_hw);
}
static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&edp_drv->lock, flags);
edp_write(edp_drv->base + 0x308, 0x0);
edp_write(edp_drv->base + 0x30c, 0x0);
+ spin_unlock_irqrestore(&edp_drv->lock, flags);
mdss_disable_irq(&mdss_edp_hw);
}
@@ -679,7 +938,6 @@
{
int ret = 0;
-
edp_drv->gpio_panel_hpd = of_get_named_gpio_flags(
edp_drv->pdev->dev.of_node, "gpio-panel-hpd", 0,
&edp_drv->hpd_flags);
@@ -769,7 +1027,11 @@
edp_drv->pdev = pdev;
edp_drv->pdev->id = 1;
edp_drv->clk_on = 0;
- edp_drv->train_start = 0; /* no link train yet */
+ edp_drv->aux_rate = 19200000;
+ edp_drv->mask1 = EDP_INTR_MASK1;
+ edp_drv->mask2 = EDP_INTR_MASK2;
+ mutex_init(&edp_drv->emutex);
+ spin_lock_init(&edp_drv->lock);
ret = mdss_edp_get_base_address(edp_drv);
if (ret)
@@ -801,34 +1063,55 @@
mdss_edp_event_setup(edp_drv);
+ edp_drv->cont_splash = of_property_read_bool(pdev->dev.of_node,
+ "qcom,cont-splash-enabled");
+
+ pr_debug("%s:cont_splash=%d\n", __func__, edp_drv->cont_splash);
+
/* need mdss clock to receive irq */
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ if (!edp_drv->cont_splash)
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
/* only need aux and ahb clock for aux channel */
mdss_edp_prepare_aux_clocks(edp_drv);
mdss_edp_aux_clk_enable(edp_drv);
- mdss_edp_phy_pll_reset(edp_drv->base);
- mdss_edp_aux_reset(edp_drv->base);
- mdss_edp_mainlink_reset(edp_drv->base);
- mdss_edp_phy_powerup(edp_drv->base, 1);
- mdss_edp_aux_ctrl(edp_drv->base, 1);
+
+ if (!edp_drv->cont_splash) {
+ mdss_edp_phy_pll_reset(edp_drv);
+ mdss_edp_aux_reset(edp_drv);
+ mdss_edp_mainlink_reset(edp_drv);
+ mdss_edp_phy_power_ctrl(edp_drv, 1);
+ mdss_edp_aux_ctrl(edp_drv, 1);
+ }
mdss_edp_irq_enable(edp_drv);
mdss_edp_edid_read(edp_drv, 0);
mdss_edp_dpcd_cap_read(edp_drv);
+ mdss_edp_fill_link_cfg(edp_drv);
mdss_edp_irq_disable(edp_drv);
- mdss_edp_aux_ctrl(edp_drv->base, 0);
+ if (!edp_drv->cont_splash) {
+ mdss_edp_aux_ctrl(edp_drv, 0);
+ mdss_edp_phy_power_ctrl(edp_drv, 0);
+ }
+
mdss_edp_aux_clk_disable(edp_drv);
- mdss_edp_phy_powerup(edp_drv->base, 0);
mdss_edp_unprepare_aux_clocks(edp_drv);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ if (!edp_drv->cont_splash)
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ if (edp_drv->cont_splash) { /* vote for clocks */
+ mdss_edp_prepare_clocks(edp_drv);
+ mdss_edp_clk_enable(edp_drv);
+ }
mdss_edp_device_register(edp_drv);
+ pr_info("%s: done\n", __func__);
+
return 0;
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
index 33b899f..0c0200d 100644
--- a/drivers/video/msm/mdss/mdss_edp.h
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -57,6 +57,8 @@
#define EDP_INTR_ACK_SHIFT 1
#define EDP_INTR_MASK_SHIFT 2
+#define EDP_MAX_LANE 4
+
/* isr */
#define EDP_INTR_HPD BIT(0)
#define EDP_INTR_AUX_I2C_DONE BIT(3)
@@ -137,8 +139,27 @@
#define EV_DPCD_CAP_READ BIT(2)
#define EV_DPCD_STATUS_READ BIT(3)
#define EV_LINK_TRAIN BIT(4)
+#define EV_IDLE_PATTERNS_SENT BIT(30)
#define EV_VIDEO_READY BIT(31)
+/* edp state ctrl */
+#define ST_TRAIN_PATTERN_1 BIT(0)
+#define ST_TRAIN_PATTERN_2 BIT(1)
+#define ST_TRAIN_PATTERN_3 BIT(2)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(3)
+#define ST_PRBS7 BIT(4)
+#define ST_CUSTOM_80_BIT_PATTERN BIT(5)
+#define ST_SEND_VIDEO BIT(6)
+#define ST_PUSH_IDLE BIT(7)
+
+/* sink power state */
+#define SINK_POWER_ON 1
+#define SINK_POWER_OFF 2
+
+#define EDP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */
+#define EDP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */
+#define EDP_LINK_RATE_MAX EDP_LINK_RATE_270
+
struct dpcd_cap {
char major;
char minor;
@@ -238,16 +259,31 @@
int (*off) (struct mdss_panel_data *pdata);
struct platform_device *pdev;
+ struct mutex emutex;
+ int clk_cnt;
+ int cont_splash;
+
/* edp specific */
unsigned char *base;
int base_size;
unsigned char *mmss_cc_base;
+ u32 mask1;
+ u32 mask2;
struct mdss_panel_data panel_data;
+ int edp_on_cnt;
+ int edp_off_cnt;
+
+ u32 pixel_rate;
+ u32 aux_rate;
+ char link_rate; /* X 27000000 for real rate */
+ char lane_cnt;
+ char train_link_rate; /* X 27000000 for real rate */
+ char train_lane_cnt;
+
struct edp_edid edid;
struct dpcd_cap dpcd;
- int train_start;
/* regulators */
struct regulator *vdda_vreg;
@@ -275,6 +311,8 @@
/* aux */
struct completion aux_comp;
struct completion train_comp;
+ struct completion idle_comp;
+ struct completion video_comp;
struct mutex aux_mutex;
u32 aux_cmd_busy;
u32 aux_cmd_i2c;
@@ -286,8 +324,6 @@
char txbuf[256];
char rxbuf[256];
struct dpcd_link_status link_status;
- char link_rate;
- char lane_cnt;
char v_level;
char p_level;
/* transfer unit */
@@ -303,12 +339,9 @@
u32 event_gndx;
u32 event_todo_list[HPD_EVENT_MAX];
spinlock_t event_lock;
+ spinlock_t lock;
};
-void mdss_edp_phy_sw_reset(unsigned char *base);
-void mdss_edp_pll_configure(unsigned char *base, int rate);
-void mdss_edp_enable_mainlink(unsigned char *base, int enable);
-void mdss_edp_phy_powerup(unsigned char *base, int enable);
int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
@@ -319,29 +352,22 @@
void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv);
int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
-void mdss_edp_config_clk(unsigned char *base, unsigned char *mmss_cc_base);
-void mdss_edp_unconfig_clk(unsigned char *base,
- unsigned char *mmss_cc_base);
void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *edp);
-void mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *edp);
+int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *edp);
void mdss_edp_edid_read(struct mdss_edp_drv_pdata *edp, int block);
int mdss_edp_link_train(struct mdss_edp_drv_pdata *edp);
void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *edp, u32 isr);
void edp_aux_native_handler(struct mdss_edp_drv_pdata *edp, u32 isr);
void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep);
-void mdss_edp_enable_aux(unsigned char *edp_base, int enable);
-void mdss_edp_timing_engine_ctrl(unsigned char *edp_base, int enable);
-void mdss_edp_mainlink_ctrl(unsigned char *edp_base, int enable);
-void mdss_edp_mainlink_reset(unsigned char *edp_base);
-void mdss_edp_aux_reset(unsigned char *edp_base);
-void mdss_edp_aux_ctrl(unsigned char *edp_base, int enable);
-void mdss_edp_phy_pll_reset(unsigned char *edp_base);
-int mdss_edp_phy_pll_ready(unsigned char *edp_base);
-int mdss_edp_phy_ready(unsigned char *edp_base);
-void mdss_edp_lane_power_ctrl(unsigned char *edp_base, int max_lane, int up);
-void mdss_edp_phy_vm_pe_init(unsigned char *edp_base);
-void mdss_edp_clock_synchrous(unsigned char *edp_base, int sync);
+void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep);
+void mdss_edp_sink_power_down(struct mdss_edp_drv_pdata *ep);
+void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state);
+int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state);
+void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up);
+void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep);
+
+void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base);
#endif /* MDSS_EDP_H */
diff --git a/drivers/video/msm/mdss/mdss_edp_aux.c b/drivers/video/msm/mdss/mdss_edp_aux.c
index 6d8e2c2..bbcee19 100644
--- a/drivers/video/msm/mdss/mdss_edp_aux.c
+++ b/drivers/video/msm/mdss/mdss_edp_aux.c
@@ -368,7 +368,6 @@
char *bp;
int i;
char csum = 0;
- int ret = 0;
bp = buf;
if (len < 128) {
@@ -389,7 +388,7 @@
return -EINVAL;
}
- return ret;
+ return 0;
}
@@ -411,7 +410,7 @@
edid->id_name[2] = 'A' + data - 1;
edid->id_name[3] = 0;
- pr_debug("%s: edid manufacturer = %s", __func__, edid->id_name);
+ pr_debug("%s: edid manufacturer = %s\n", __func__, edid->id_name);
}
void edp_extract_edid_product(struct edp_edid *edid, char *buf)
@@ -427,21 +426,21 @@
data <<= 8;
edid->id_product |= data;
- pr_debug("%s: edid product = 0x%x", __func__, edid->id_product);
+ pr_debug("%s: edid product = 0x%x\n", __func__, edid->id_product);
};
void edp_extract_edid_version(struct edp_edid *edid, char *buf)
{
edid->version = buf[0x12];
edid->revision = buf[0x13];
- pr_debug("%s: edid version = %d.%d", __func__, edid->version,
+ pr_debug("%s: edid version = %d.%d\n", __func__, edid->version,
edid->revision);
};
void edp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf)
{
edid->ext_block_cnt = buf[0x7e];
- pr_debug("%s: edid extension = %d", __func__,
+ pr_debug("%s: edid extension = %d\n", __func__,
edid->ext_block_cnt);
};
@@ -461,7 +460,7 @@
pr_debug("%s: Digital Video intf=%d color_depth=%d\n",
__func__, edid->video_intf, edid->color_depth);
} else {
- pr_err("%s: Error, Analog video interface", __func__);
+ pr_err("%s: Error, Analog video interface\n", __func__);
}
};
@@ -485,7 +484,7 @@
}
}
- pr_debug("%s: edid dpm=%d color_format=%d", __func__,
+ pr_debug("%s: edid dpm=%d color_format=%d\n", __func__,
edid->dpm, edid->color_format);
};
@@ -567,6 +566,7 @@
dp->h_border = *bp++; /* byte 0x45 */
dp->v_border = *bp++; /* byte 0x46 */
+ /* progressive or interlaved */
dp->interlaced = *bp & 0x80; /* byte 0x47 */
dp->stereo = *bp & 0x60;
@@ -642,12 +642,13 @@
pr_debug("%s: ret=%d\n", __func__, ret);
if (ret >= 0)
break;
- pr_debug("%s: failed in write\n", __func__);
msleep(100);
}
- if (cnt == 0)
+ if (cnt <= 0) {
+ pr_err("%s: aux chan NOT ready\n", __func__);
return 0;
+ }
return 1;
}
@@ -718,14 +719,15 @@
data = *bp++; /* byte 1 */
/* 162, 270 and 540 MB, symbol rate, NOT bit rate */
- cap->max_link_rate = data * 27;
+ cap->max_link_rate = data;
if (--rlen <= 0)
return;
pr_debug("%s: link_rate=%d\n", __func__, cap->max_link_rate);
data = *bp++; /* byte 2 */
if (data & BIT(7))
- cap->flags |= DPCD_ENHANCED_FRAME;
+ cap->enhanced_frame++;
+
if (data & 0x40)
cap->flags |= DPCD_TPS3;
data &= 0x0f;
@@ -789,7 +791,9 @@
pr_debug("%s: scrambler_reset=%d\n", __func__,
cap->scrambler_reset);
- cap->enhanced_frame = data & BIT(1);
+ if (data & BIT(1))
+ cap->enhanced_frame++;
+
pr_debug("%s: enhanced_framing=%d\n", __func__,
cap->enhanced_frame);
if (--rlen <= 0)
@@ -804,7 +808,7 @@
cap->training_read_interval);
}
-static void edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len)
+static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len)
{
char *bp;
char data;
@@ -815,9 +819,9 @@
pr_debug("%s: len=%d", __func__, len);
/* skip byte 0x200 and 0x201 */
rlen = edp_aux_read_buf(ep, 0x202, len, 0);
- if (rlen <= 0) {
+ if (rlen < len) {
pr_err("%s: edp aux read failed\n", __func__);
- return;
+ return 0;
}
rp = &ep->rxp;
bp = rp->data;
@@ -825,26 +829,18 @@
data = *bp++; /* byte 0x202 */
sp->lane_01_status = data; /* lane 0, 1 */
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 0x203 */
sp->lane_23_status = data; /* lane 2, 3 */
- if (--rlen <= 0)
- return;
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));
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 0x205 */
sp->port_0_in_sync = (data & BIT(0));
sp->port_1_in_sync = (data & BIT(1));
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 0x206 */
sp->req_voltage_swing[0] = data & 0x03;
@@ -854,8 +850,6 @@
sp->req_voltage_swing[1] = data & 0x03;
data >>= 2;
sp->req_pre_emphasis[1] = data & 0x03;
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 0x207 */
sp->req_voltage_swing[2] = data & 0x03;
@@ -865,16 +859,23 @@
sp->req_voltage_swing[3] = data & 0x03;
data >>= 2;
sp->req_pre_emphasis[3] = data & 0x03;
+
+ return len;
}
static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep)
{
char buf[4];
int len = 0;
+ struct dpcd_cap *cap;
+
+ cap = &ep->dpcd;
pr_debug("%s: bw=%x lane=%d\n", __func__, ep->link_rate, ep->lane_cnt);
buf[0] = ep->link_rate;
buf[1] = ep->lane_cnt;
+ if (cap->enhanced_frame)
+ buf[1] |= 0x80;
len = edp_aux_write_buf(ep, 0x100, buf, 2, 0);
return len;
@@ -901,13 +902,6 @@
return edp_aux_write_buf(ep, 0x103, buf, 4, 0);
}
-static int edp_powerstate_write(struct mdss_edp_drv_pdata *ep,
- char powerstate)
-{
- pr_debug("%s: state=%d\n", __func__, powerstate);
- return edp_aux_write_buf(ep, 0x600, &powerstate, 1, 0);
-}
-
static int edp_train_pattern_set_write(struct mdss_edp_drv_pdata *ep,
int pattern)
{
@@ -923,8 +917,6 @@
u32 mask;
u32 data;
- pr_debug("%s:\n", __func__);
-
if (ep->lane_cnt == 1) {
mask = 0x01; /* lane 0 */
data = ep->link_status.lane_01_status;
@@ -953,8 +945,10 @@
pr_debug("%s:\n", __func__);
- if (!ep->link_status.interlane_align_done) /* not align */
+ if (!ep->link_status.interlane_align_done) { /* not align */
+ pr_err("%s: interlane align failed\n", __func__);
return 0;
+ }
if (ep->lane_cnt == 1) {
mask = 0x7;
@@ -1154,8 +1148,47 @@
return ret;
}
-static int edp_link_rate_shift(struct mdss_edp_drv_pdata *ep)
+static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep)
{
+ u32 prate, lrate;
+ int rate, lane, max_lane;
+ int changed = 0;
+
+ rate = ep->link_rate;
+ lane = ep->lane_cnt;
+ max_lane = ep->dpcd.max_lane_count;
+
+ prate = ep->pixel_rate;
+ prate /= 1000; /* avoid using 64 biits */
+ prate *= ep->bpp;
+ prate /= 8; /* byte */
+
+ if (rate > EDP_LINK_RATE_162 && rate <= EDP_LINK_RATE_MAX) {
+ rate -= 4; /* reduce rate */
+ changed++;
+ }
+
+ if (changed) {
+ if (lane >= 1 && lane < max_lane)
+ lane <<= 1; /* increase lane */
+
+ lrate = 270000000; /* 270M */
+ lrate /= 1000; /* avoid using 64 bits */
+ lrate *= rate;
+ lrate /= 10; /* byte, 10 bits --> 8 bits */
+ lrate *= lane;
+
+ pr_debug("%s: new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
+ __func__, lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
+
+ if (lrate > prate) {
+ ep->link_rate = rate;
+ ep->lane_cnt = lane;
+ pr_debug("%s: new rate=%d %d\n", __func__, rate, lane);
+ return 0;
+ }
+ }
+
/* add calculation later */
return -EINVAL;
}
@@ -1180,23 +1213,23 @@
return ret;
}
- /* start with max rate and lane */
- ep->lane_cnt = ep->dpcd.max_lane_count;
- ep->link_rate = ep->dpcd.max_link_rate;
edp_write(ep->base + EDP_MAINLINK_CTRL, 0x1);
+ mdss_edp_sink_power_state(ep, SINK_POWER_ON);
+
train_start:
ep->v_level = 0; /* start from default level */
ep->p_level = 0;
edp_cap_lane_rate_set(ep);
+ mdss_edp_config_ctrl(ep);
+ mdss_edp_lane_power_ctrl(ep, 1);
edp_clear_training_pattern(ep);
usleep(ep->dpcd.training_read_interval);
- edp_powerstate_write(ep, 1);
ret = edp_start_link_train_1(ep);
if (ret < 0) {
- if (edp_link_rate_shift(ep) == 0) {
+ if (edp_link_rate_down_shift(ep) == 0) {
goto train_start;
} else {
pr_err("%s: Training 1 failed", __func__);
@@ -1210,7 +1243,7 @@
edp_clear_training_pattern(ep);
ret = edp_start_link_train_2(ep);
if (ret < 0) {
- if (edp_link_rate_shift(ep) == 0) {
+ if (edp_link_rate_down_shift(ep) == 0) {
goto train_start;
} else {
pr_err("%s: Training 2 failed", __func__);
@@ -1221,6 +1254,7 @@
pr_debug("%s: Training 2 completed successfully", __func__);
+ mdss_edp_state_ctrl(ep, ST_SEND_VIDEO);
clear:
edp_clear_training_pattern(ep);
@@ -1233,9 +1267,33 @@
edp_sink_capability_read(ep, 16);
}
-void mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep)
+int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep)
{
- edp_link_status_read(ep, 6);
+ struct dpcd_link_status *sp;
+ int ret = 0; /* not sync */
+
+ ret = edp_link_status_read(ep, 6);
+
+ if (ret) {
+ sp = &ep->link_status;
+ ret = sp->port_0_in_sync; /* 1 == sync */
+ }
+
+ return ret;
+}
+
+void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep)
+{
+ struct display_timing_desc *dp;
+
+ dp = &ep->edid.timing[0];
+ ep->pixel_rate = dp->pclk;
+ ep->lane_cnt = ep->dpcd.max_lane_count;
+ ep->link_rate = ep->dpcd.max_link_rate;
+
+ pr_debug("%s: pclk=%d rate=%d lane=%d\n", __func__,
+ ep->pixel_rate, ep->link_rate, ep->lane_cnt);
+
}
void mdss_edp_edid_read(struct mdss_edp_drv_pdata *ep, int block)
@@ -1243,6 +1301,15 @@
edp_sink_edid_read(ep, block);
}
+int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state)
+{
+ int ret;
+
+ ret = edp_aux_write_buf(ep, 0x600, &state, 1, 0);
+ pr_debug("%s: state=%d ret=%d\n", __func__, state, ret);
+ return ret;
+}
+
int mdss_edp_link_train(struct mdss_edp_drv_pdata *ep)
{
return edp_aux_link_train(ep);
@@ -1253,7 +1320,10 @@
mutex_init(&ep->aux_mutex);
init_completion(&ep->aux_comp);
init_completion(&ep->train_comp);
+ init_completion(&ep->idle_comp);
+ init_completion(&ep->video_comp);
complete(&ep->train_comp); /* make non block at first time */
+ complete(&ep->video_comp); /* make non block at first time */
edp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf));
edp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf));
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 64b7e95..769f9b2 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -117,6 +117,12 @@
MDSS_MDP_MAX_CSC
};
+struct splash_pipe_cfg {
+ int width;
+ int height;
+ int mixer;
+};
+
struct mdss_mdp_ctl;
typedef void (*mdp_vsync_handler_t)(struct mdss_mdp_ctl *, ktime_t);
@@ -461,6 +467,7 @@
irqreturn_t mdss_mdp_isr(int irq, void *ptr);
int mdss_iommu_attach(struct mdss_data_type *mdata);
int mdss_iommu_dettach(struct mdss_data_type *mdata);
+int mdss_mdp_scan_cont_splash(void);
void mdss_mdp_irq_clear(struct mdss_data_type *mdata,
u32 intr_type, u32 intf_num);
int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num);
@@ -522,6 +529,8 @@
int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num,
struct mdss_mdp_pipe *pipe);
+int mdss_mdp_scan_pipes(void);
+
struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 9fd2c8b..1fedd6e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -615,6 +615,7 @@
{
switch (ctl->panel_data->panel_info.type) {
case MIPI_VIDEO_PANEL:
+ case EDP_PANEL:
return mdss_mdp_video_reconfigure_splash_done(ctl, handoff);
case MIPI_CMD_PANEL:
return mdss_mdp_cmd_reconfigure_splash_done(ctl, handoff);
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index b570136..14b486a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -562,10 +562,14 @@
if ((pipe->flags & MDP_DEINTERLACE) && !pipe->scale.enable_pxl_ext) {
if (pipe->flags & MDP_SOURCE_ROTATED_90) {
+ pipe->src.x = DIV_ROUND_UP(pipe->src.x, 2);
+ pipe->src.x &= ~1;
pipe->src.w /= 2;
pipe->img_width /= 2;
} else {
pipe->src.h /= 2;
+ pipe->src.y = DIV_ROUND_UP(pipe->src.y, 2);
+ pipe->src.y &= ~1;
}
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index 057914b..e2e41bb 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -437,6 +437,7 @@
rot->flags |= MDP_DEINTERLACE;
rot->src_rect.h /= 2;
rot->src_rect.y = DIV_ROUND_UP(rot->src_rect.y, 2);
+ rot->src_rect.y &= ~1;
}
rot->dst = rot->src_rect;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 62704a9..a63275b 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -665,215 +665,6 @@
}
-void mdss_edp_timing_engine_ctrl(unsigned char *edp_base, int enable)
-{
- /* should eb last reg to program */
- edp_write(edp_base + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
-}
-
-void mdss_edp_mainlink_ctrl(unsigned char *edp_base, int enable)
-{
- edp_write(edp_base + 0x04, enable); /* EDP_MAINLINK_CTRL */
-}
-
-void mdss_edp_mainlink_reset(unsigned char *edp_base)
-{
- edp_write(edp_base + 0x04, 0x02); /* EDP_MAINLINK_CTRL */
- usleep(1000);
- edp_write(edp_base + 0x04, 0); /* EDP_MAINLINK_CTRL */
-}
-
-void mdss_edp_aux_reset(unsigned char *edp_base)
-{
- /*reset AUX */
- edp_write(edp_base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
- usleep(1000);
- edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
-}
-
-void mdss_edp_aux_ctrl(unsigned char *edp_base, int enable)
-{
- u32 data;
-
- data = edp_read(edp_base + 0x300);
- if (enable)
- data |= 0x01;
- else
- data |= ~0x01;
- edp_write(edp_base + 0x300, data); /* EDP_AUX_CTRL */
-}
-
-void mdss_edp_phy_pll_reset(unsigned char *edp_base)
-{
- /* EDP_PHY_CTRL */
- edp_write(edp_base + 0x74, 0x005); /* bit 0, 2 */
- usleep(1000);
- edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
-}
-
-int mdss_edp_phy_pll_ready(unsigned char *edp_base)
-{
- int cnt;
- u32 status;
-
- cnt = 10;
- while (cnt--) {
- status = edp_read(edp_base + 0x6c0);
- if (status & 0x01)
- break;
- usleep(100);
- }
-
- if (cnt == 0) {
- pr_err("%s: PLL NOT ready\n", __func__);
- return 0;
- } else
- return 1;
-}
-
-int mdss_edp_phy_ready(unsigned char *edp_base)
-{
- u32 status;
-
- status = edp_read(edp_base + 0x598);
- status &= 0x01;
-
- return status;
-}
-
-void mdss_edp_phy_powerup(unsigned char *edp_base, int enable)
-{
- if (enable) {
- /* EDP_PHY_EDPPHY_GLB_PD_CTL */
- edp_write(edp_base + 0x52c, 0x3f);
- /* EDP_PHY_EDPPHY_GLB_CFG */
- edp_write(edp_base + 0x528, 0x1);
- /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
- edp_write(edp_base + 0x620, 0xf);
- } else {
- /* EDP_PHY_EDPPHY_GLB_PD_CTL */
- edp_write(edp_base + 0x52c, 0xc0);
- }
-}
-
-void mdss_edp_pll_configure(unsigned char *edp_base, int rate)
-{
- if (rate == 810000000) {
- edp_write(edp_base + 0x60c, 0x18);
- edp_write(edp_base + 0x664, 0x5);
- edp_write(edp_base + 0x600, 0x0);
- edp_write(edp_base + 0x638, 0x36);
- edp_write(edp_base + 0x63c, 0x69);
- edp_write(edp_base + 0x640, 0xff);
- edp_write(edp_base + 0x644, 0x2f);
- edp_write(edp_base + 0x648, 0x0);
- edp_write(edp_base + 0x66c, 0x0a);
- edp_write(edp_base + 0x674, 0x01);
- edp_write(edp_base + 0x684, 0x5a);
- edp_write(edp_base + 0x688, 0x0);
- edp_write(edp_base + 0x68c, 0x60);
- edp_write(edp_base + 0x690, 0x0);
- edp_write(edp_base + 0x694, 0x2a);
- edp_write(edp_base + 0x698, 0x3);
- edp_write(edp_base + 0x65c, 0x10);
- edp_write(edp_base + 0x660, 0x1a);
- edp_write(edp_base + 0x604, 0x0);
- edp_write(edp_base + 0x624, 0x0);
- edp_write(edp_base + 0x628, 0x0);
-
- edp_write(edp_base + 0x620, 0x1);
- edp_write(edp_base + 0x620, 0x5);
- edp_write(edp_base + 0x620, 0x7);
- edp_write(edp_base + 0x620, 0xf);
-
- } else if (rate == 138530000) {
- edp_write(edp_base + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
- edp_write(edp_base + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
- edp_write(edp_base + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
- edp_write(edp_base + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
- edp_write(edp_base + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
- edp_write(edp_base + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
- edp_write(edp_base + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
- edp_write(edp_base + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
- edp_write(edp_base + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
- edp_write(edp_base + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
- edp_write(edp_base + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
- edp_write(edp_base + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
- edp_write(edp_base + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
- edp_write(edp_base + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
- edp_write(edp_base + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
- edp_write(edp_base + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
- edp_write(edp_base + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
- edp_write(edp_base + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
- edp_write(edp_base + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
- edp_write(edp_base + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
- edp_write(edp_base + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
- edp_write(edp_base + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
- edp_write(edp_base + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
- edp_write(edp_base + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
-
- edp_write(edp_base + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
- edp_write(edp_base + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
- edp_write(edp_base + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
- edp_write(edp_base + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
- } else {
- pr_err("%s: rate=%d is NOT supported\n", __func__, rate);
- }
-}
-
-void mdss_edp_enable_aux(unsigned char *edp_base, int enable)
-{
- if (!enable) {
- edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
- return;
- }
-
- /*reset AUX */
- edp_write(edp_base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
- edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
-
- /* Enable AUX */
- edp_write(edp_base + 0x300, BIT(0)); /* EDP_AUX_CTRL */
-
- edp_write(edp_base + 0x550, 0x2c); /* AUX_CFG0 */
- edp_write(edp_base + 0x308, 0xffffffff); /* INTR_STATUS */
- edp_write(edp_base + 0x568, 0xff); /* INTR_MASK */
-}
-
-void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable)
-{
- u32 data;
-
- data = edp_read(edp_base + 0x004);
- data &= ~BIT(0);
-
- if (enable) {
- data |= 0x1;
- edp_write(edp_base + 0x004, data);
- edp_write(edp_base + 0x004, 0x1);
- } else {
- data |= 0x0;
- edp_write(edp_base + 0x004, data);
- }
-}
-
-void mdss_edp_lane_power_ctrl(unsigned char *edp_base, int max_lane, int up)
-{
- int i, off;
- u32 data;
-
- if (up)
- data = 0; /* power up */
- else
- data = 0x7; /* power down */
-
- /* EDP_PHY_EDPPHY_LNn_PD_CTL */
- for (i = 0; i < max_lane; i++) {
- off = 0x40 * i;
- edp_write(edp_base + 0x404 + off , data);
- }
-}
-
void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv)
{
if (edp_drv->aux_clk)
@@ -968,18 +759,18 @@
return 0;
}
- if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0)
+ if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0)
+ pr_err("%s: link_clk - clk_set_rate failed\n",
+ __func__);
+
+ if (clk_set_rate(edp_drv->aux_clk, edp_drv->aux_rate) < 0)
pr_err("%s: aux_clk - clk_set_rate failed\n",
__func__);
- if (clk_set_rate(edp_drv->pixel_clk, 138500000) < 0)
+ if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0)
pr_err("%s: pixel_clk - clk_set_rate failed\n",
__func__);
- if (clk_set_rate(edp_drv->link_clk, 270000000) < 0)
- pr_err("%s: link_clk - clk_set_rate failed\n",
- __func__);
-
ret = clk_enable(edp_drv->aux_clk);
if (ret) {
pr_err("%s: Failed to enable aux clk\n", __func__);
@@ -1034,20 +825,20 @@
{
int ret;
- ret = clk_prepare(edp_drv->aux_clk);
- if (ret) {
- pr_err("%s: Failed to prepare aux clk\n", __func__);
- goto c2;
- }
ret = clk_prepare(edp_drv->ahb_clk);
if (ret) {
pr_err("%s: Failed to prepare ahb clk\n", __func__);
goto c1;
}
+ ret = clk_prepare(edp_drv->aux_clk);
+ if (ret) {
+ pr_err("%s: Failed to prepare aux clk\n", __func__);
+ goto c2;
+ }
return 0;
c1:
- clk_unprepare(edp_drv->aux_clk);
+ clk_unprepare(edp_drv->ahb_clk);
c2:
return ret;
@@ -1063,19 +854,20 @@
{
int ret;
+ /* ahb clock should be first one to enable */
+ ret = clk_prepare(edp_drv->ahb_clk);
+ if (ret) {
+ pr_err("%s: Failed to prepare ahb clk\n", __func__);
+ goto c4;
+ }
ret = clk_prepare(edp_drv->aux_clk);
if (ret) {
pr_err("%s: Failed to prepare aux clk\n", __func__);
- goto c4;
+ goto c3;
}
ret = clk_prepare(edp_drv->pixel_clk);
if (ret) {
pr_err("%s: Failed to prepare pixel clk\n", __func__);
- goto c3;
- }
- ret = clk_prepare(edp_drv->ahb_clk);
- if (ret) {
- pr_err("%s: Failed to prepare ahb clk\n", __func__);
goto c2;
}
ret = clk_prepare(edp_drv->link_clk);
@@ -1086,11 +878,11 @@
return 0;
c1:
- clk_unprepare(edp_drv->ahb_clk);
-c2:
clk_unprepare(edp_drv->pixel_clk);
-c3:
+c2:
clk_unprepare(edp_drv->aux_clk);
+c3:
+ clk_unprepare(edp_drv->ahb_clk);
c4:
return ret;
}
@@ -1099,81 +891,27 @@
{
clk_unprepare(edp_drv->aux_clk);
clk_unprepare(edp_drv->pixel_clk);
- clk_unprepare(edp_drv->ahb_clk);
clk_unprepare(edp_drv->link_clk);
+ /* ahb clock should be last one to disable */
+ clk_unprepare(edp_drv->ahb_clk);
}
-void mdss_edp_enable_pixel_clk(unsigned char *edp_base,
- unsigned char *mmss_cc_base, int enable)
+void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base)
{
- if (!enable) {
- edp_write(mmss_cc_base + 0x032c, 0); /* CBCR */
- return;
- }
+ u32 da4, da0, d32c;
+ u32 dc4, dc0, d330;
- edp_write(edp_base + 0x624, 0x1); /* PostDiv2 */
+ /* pixel clk */
+ da0 = edp_read(mmss_cc_base + 0x0a0);
+ da4 = edp_read(mmss_cc_base + 0x0a4);
+ d32c = edp_read(mmss_cc_base + 0x32c);
- /* Configuring MND for Pixel */
- edp_write(mmss_cc_base + 0x00a8, 0x3f); /* M value */
- edp_write(mmss_cc_base + 0x00ac, 0xb); /* N value */
- edp_write(mmss_cc_base + 0x00b0, 0x0); /* D value */
+ /* main link clk */
+ dc0 = edp_read(mmss_cc_base + 0x0c0);
+ dc4 = edp_read(mmss_cc_base + 0x0c4);
+ d330 = edp_read(mmss_cc_base + 0x330);
- /* CFG RCGR */
- edp_write(mmss_cc_base + 0x00a4, (5 << 8) | (2 << 12));
- edp_write(mmss_cc_base + 0x00a0, 3); /* CMD RCGR */
+ pr_err("%s: da0=%x da4=%x d32c=%x dc0=%x dc4=%x d330=%x\n", __func__,
+ (int)da0, (int)da4, (int)d32c, (int)dc0, (int)dc4, (int)d330);
- edp_write(mmss_cc_base + 0x032c, 1); /* CBCR */
-}
-
-void mdss_edp_enable_link_clk(unsigned char *mmss_cc_base, int enable)
-{
- if (!enable) {
- edp_write(mmss_cc_base + 0x0330, 0); /* CBCR */
- return;
- }
-
- edp_write(mmss_cc_base + 0x00c4, (4 << 8)); /* CFG RCGR */
- edp_write(mmss_cc_base + 0x00c0, 3); /* CMD RCGR */
-
- edp_write(mmss_cc_base + 0x0330, 1); /* CBCR */
-}
-
-void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base)
-{
- mdss_edp_enable_link_clk(mmss_cc_base, 1);
- mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 1);
-}
-
-void mdss_edp_unconfig_clk(unsigned char *edp_base,
- unsigned char *mmss_cc_base)
-{
- mdss_edp_enable_link_clk(mmss_cc_base, 0);
- mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 0);
-}
-
-void mdss_edp_clock_synchrous(unsigned char *edp_base, int sync)
-{
- u32 data;
-
- /* EDP_MISC1_MISC0 */
- data = edp_read(edp_base + 0x02c);
-
- if (sync)
- data |= 0x01;
- else
- data &= ~0x01;
-
- /* EDP_MISC1_MISC0 */
- edp_write(edp_base + 0x2c, data);
-}
-
-/* voltage mode and pre emphasis cfg */
-void mdss_edp_phy_vm_pe_init(unsigned char *edp_base)
-{
- /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
- edp_write(edp_base + 0x510, 0x3); /* vm only */
- /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
- edp_write(edp_base + 0x514, 0x64);
- /* EDP_PHY_EDPPHY_GLB_MISC9 */
- edp_write(edp_base + 0x518, 0x6c);
}
diff --git a/include/linux/input.h b/include/linux/input.h
index d4cdb02..558178b 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -167,6 +167,7 @@
#define INPUT_PROP_DIRECT 0x01 /* direct input devices */
#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */
#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */
+#define INPUT_PROP_NO_DUMMY_RELEASE 0x04 /* no dummy event */
#define INPUT_PROP_MAX 0x1f
#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 9f1fedf..d4c0534 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1463,10 +1463,11 @@
zone = page_zone(page);
mt = get_pageblock_migratetype(page);
- if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt)) {
+ if (mt != MIGRATE_ISOLATE) {
/* Obey watermarks as if the page was being allocated */
watermark = low_wmark_pages(zone) + (1 << order);
- if (!zone_watermark_ok(zone, 0, watermark, 0, 0))
+ if (!is_migrate_cma(mt) &&
+ !zone_watermark_ok(zone, 0, watermark, 0, 0))
return 0;
__mod_zone_freepage_state(zone, -(1UL << order), mt);