diff options
Diffstat (limited to 'drivers')
88 files changed, 7982 insertions, 4757 deletions
diff --git a/drivers/base/regmap/regmap-swr.c b/drivers/base/regmap/regmap-swr.c index 5ea67421ed85..027cbfc505ab 100644 --- a/drivers/base/regmap/regmap-swr.c +++ b/drivers/base/regmap/regmap-swr.c @@ -23,15 +23,15 @@ static int regmap_swr_gather_write(void *context, const void *reg, size_t reg_size, - const void *val, size_t val_size) + const void *val, size_t val_len) { struct device *dev = context; struct swr_device *swr = to_swr_device(dev); struct regmap *map = dev_get_regmap(dev, NULL); size_t addr_bytes = map->format.reg_bytes; - int ret = 0; - int i; - u32 reg_addr = 0; + size_t val_bytes; + int i, ret = 0; + u16 reg_addr = 0; if (swr == NULL) { dev_err(dev, "%s: swr device is NULL\n", __func__); @@ -43,12 +43,15 @@ static int regmap_swr_gather_write(void *context, return -EINVAL; } reg_addr = *(u16 *)reg; - for (i = 0; i < val_size; i++) { - ret = swr_write(swr, swr->dev_num, (reg_addr+i), - (u32 *)(val+i)); + val_bytes = map->format.val_bytes; + /* val_len = val_bytes * val_count */ + for (i = 0; i < (val_len / val_bytes); i++) { + reg_addr = reg_addr + i; + val = (u8 *)val + (val_bytes * i); + ret = swr_write(swr, swr->dev_num, reg_addr, val); if (ret < 0) { dev_err(dev, "%s: write reg 0x%x failed, err %d\n", - __func__, (reg_addr+i), ret); + __func__, reg_addr, ret); break; } } @@ -153,7 +156,7 @@ static int regmap_swr_read(void *context, struct regmap *map = dev_get_regmap(dev, NULL); size_t addr_bytes = map->format.reg_bytes; int ret = 0; - u32 reg_addr = 0; + u16 reg_addr = 0; if (swr == NULL) { dev_err(dev, "%s: swr is NULL\n", __func__); @@ -164,7 +167,7 @@ static int regmap_swr_read(void *context, __func__, reg_size); return -EINVAL; } - reg_addr = *(u32 *)reg; + reg_addr = *(u16 *)reg; ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size); if (ret < 0) dev_err(dev, "%s: codec reg 0x%x read failed %d\n", diff --git a/drivers/clk/msm/clock-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c index 7230c7a2bc04..7cec9be1f42c 100644 --- a/drivers/clk/msm/clock-gpu-cobalt.c +++ b/drivers/clk/msm/clock-gpu-cobalt.c @@ -39,8 +39,6 @@ static void __iomem *virt_base_gfx; #define gpucc_gpll0_source_val 5 #define gpu_pll0_pll_out_even_source_val 1 #define gpu_pll0_pll_out_odd_source_val 2 -#define gpu_pll1_pll_out_even_source_val 3 -#define gpu_pll1_pll_out_odd_source_val 4 #define SW_COLLAPSE_MASK BIT(0) #define GPU_CX_GDSCR_OFFSET 0x1004 @@ -157,65 +155,6 @@ static struct div_clk gpu_pll0_pll_out_odd = { }, }; -static struct alpha_pll_clk gpu_pll1_pll = { - .masks = &pll_masks_p, - .base = &virt_base_gfx, - .offset = GPUCC_GPU_PLL1_PLL_MODE, - .enable_config = 0x1, - .is_fabia = true, - .c = { - .rate = 0, - .parent = &gpucc_xo.c, - .dbg_name = "gpu_pll1_pll", - .ops = &clk_ops_fabia_alpha_pll, - VDD_GPU_PLL_FMAX_MAP1(MIN, 1300000500), - CLK_INIT(gpu_pll1_pll.c), - }, -}; - -static struct div_clk gpu_pll1_pll_out_even = { - .base = &virt_base_gfx, - .offset = GPUCC_GPU_PLL1_USER_CTL_MODE, - .mask = 0xf, - .shift = 8, - .data = { - .max_div = 8, - .min_div = 1, - .skip_odd_div = true, - .allow_div_one = true, - .rate_margin = 500, - }, - .ops = &postdiv_reg_ops, - .c = { - .parent = &gpu_pll1_pll.c, - .dbg_name = "gpu_pll1_pll_out_even", - .ops = &clk_ops_div, - .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(gpu_pll1_pll_out_even.c), - }, -}; - -static struct div_clk gpu_pll1_pll_out_odd = { - .base = &virt_base_gfx, - .offset = GPUCC_GPU_PLL0_USER_CTL_MODE, - .mask = 0xf, - .shift = 12, - .data = { - .max_div = 7, - .min_div = 3, - .skip_even_div = true, - .rate_margin = 500, - }, - .ops = &postdiv_reg_ops, - .c = { - .parent = &gpu_pll1_pll.c, - .dbg_name = "gpu_pll1_pll_out_odd", - .ops = &clk_ops_div, - .flags = CLKFLAG_NO_RATE_CACHE, - CLK_INIT(gpu_pll1_pll_out_odd.c), - }, -}; - static struct clk_freq_tbl ftbl_gfx3d_clk_src[] = { F_SLEW( 171000000, 342000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 251000000, 502000000, gpu_pll0_pll_out_even, 1, 0, 0), @@ -227,11 +166,11 @@ static struct clk_freq_tbl ftbl_gfx3d_clk_src[] = { }; static struct clk_freq_tbl ftbl_gfx3d_clk_src_v2[] = { - F_SLEW( 189000000, 378000000, gpu_pll0_pll_out_even, 1, 0, 0), - F_SLEW( 264000000, 528000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 180000000, 360000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 257000000, 514000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 342000000, 684000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 414000000, 828000000, gpu_pll0_pll_out_even, 1, 0, 0), - F_SLEW( 520000000, 1040000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 515000000, 1030000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 596000000, 1192000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 670000000, 1340000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 710000000, 1420000000, gpu_pll0_pll_out_even, 1, 0, 0), @@ -239,14 +178,14 @@ static struct clk_freq_tbl ftbl_gfx3d_clk_src_v2[] = { }; static struct clk_freq_tbl ftbl_gfx3d_clk_src_vq[] = { - F_SLEW( 185000000, 370000000, gpu_pll0_pll_out_even, 1, 0, 0), - F_SLEW( 285000000, 570000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 180000000, 360000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 265000000, 530000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 358000000, 716000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 434000000, 868000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 542000000, 1084000000, gpu_pll0_pll_out_even, 1, 0, 0), F_SLEW( 630000000, 1260000000, gpu_pll0_pll_out_even, 1, 0, 0), - F_SLEW( 670000000, 1340000000, gpu_pll1_pll_out_even, 1, 0, 0), - F_SLEW( 710000000, 1420000000, gpu_pll1_pll_out_even, 1, 0, 0), + F_SLEW( 700000000, 1400000000, gpu_pll0_pll_out_even, 1, 0, 0), + F_SLEW( 750000000, 1500000000, gpu_pll0_pll_out_even, 1, 0, 0), F_END }; @@ -659,9 +598,6 @@ static struct clk_lookup msm_clocks_gfxcc_cobalt[] = { CLK_LIST(gpu_pll0_pll), CLK_LIST(gpu_pll0_pll_out_even), CLK_LIST(gpu_pll0_pll_out_odd), - CLK_LIST(gpu_pll1_pll), - CLK_LIST(gpu_pll1_pll_out_even), - CLK_LIST(gpu_pll1_pll_out_odd), CLK_LIST(gfx3d_clk_src), CLK_LIST(gpucc_gfx3d_clk), CLK_LIST(gpucc_mx_clk), @@ -671,14 +607,12 @@ static struct clk_lookup msm_clocks_gfxcc_cobalt[] = { static void msm_gfxcc_hamster_fixup(void) { gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500; - gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500; gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_vq; } static void msm_gfxcc_cobalt_v2_fixup(void) { gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500; - gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500; gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_v2; } diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c index 288abb133743..2da10a2e4780 100644 --- a/drivers/clk/msm/clock-mmss-cobalt.c +++ b/drivers/clk/msm/clock-mmss-cobalt.c @@ -274,7 +274,7 @@ static struct rcg_clk csi0_clk_src = { .c = { .dbg_name = "csi0_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP4(LOWER, 164570000, LOW, 256000000, + VDD_DIG_FMAX_MAP4(LOWER, 164571429, LOW, 256000000, NOMINAL, 384000000, HIGH, 576000000), CLK_INIT(csi0_clk_src.c), }, @@ -292,6 +292,9 @@ static struct clk_freq_tbl ftbl_vfe_clk_src[] = { static struct clk_freq_tbl ftbl_vfe_clk_src_vq[] = { F_MM( 200000000, mmsscc_gpll0, 3, 0, 0), + F_MM( 300000000, mmsscc_gpll0, 2, 0, 0), + F_MM( 320000000, mmpll7_pll_out, 3, 0, 0), + F_MM( 384000000, mmpll4_pll_out, 2, 0, 0), F_MM( 404000000, mmpll0_pll_out, 2, 0, 0), F_MM( 480000000, mmpll7_pll_out, 2, 0, 0), F_MM( 576000000, mmpll10_pll_out, 1, 0, 0), @@ -367,16 +370,6 @@ static struct clk_freq_tbl ftbl_maxi_clk_src[] = { F_END }; -static struct clk_freq_tbl ftbl_maxi_clk_src_vq[] = { - F_MM( 19200000, mmsscc_xo, 1, 0, 0), - F_MM( 75000000, mmsscc_gpll0_div, 4, 0, 0), - F_MM( 171428571, mmsscc_gpll0, 3.5, 0, 0), - F_MM( 240000000, mmsscc_gpll0, 2.5, 0, 0), - F_MM( 323200000, mmpll0_pll_out, 2.5, 0, 0), - F_MM( 406000000, mmpll1_pll_out, 2, 0, 0), - F_END -}; - static struct rcg_clk maxi_clk_src = { .cmd_rcgr_reg = MMSS_MAXI_CMD_RCGR, .set_rate = set_rate_hid, @@ -592,18 +585,11 @@ static struct clk_freq_tbl ftbl_fd_core_clk_src[] = { F_END }; -static struct clk_freq_tbl ftbl_fd_core_clk_src_v2[] = { - F_MM( 100000000, mmsscc_gpll0, 6, 0, 0), - F_MM( 404000000, mmpll0_pll_out, 2, 0, 0), - F_MM( 480000000, mmpll7_pll_out, 2, 0, 0), - F_MM( 576000000, mmpll10_pll_out, 1, 0, 0), - F_END -}; - static struct clk_freq_tbl ftbl_fd_core_clk_src_vq[] = { F_MM( 100000000, mmsscc_gpll0, 6, 0, 0), F_MM( 200000000, mmsscc_gpll0, 3, 0, 0), - F_MM( 400000000, mmsscc_gpll0, 1.5, 0, 0), + F_MM( 404000000, mmpll0_pll_out, 2, 0, 0), + F_MM( 480000000, mmpll7_pll_out, 2, 0, 0), F_MM( 576000000, mmpll10_pll_out, 1, 0, 0), F_END }; @@ -2677,60 +2663,81 @@ static void msm_mmsscc_hamster_fixup(void) vfe1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000; csi0_clk_src.freq_tbl = ftbl_csi_clk_src_vq; - csi0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi0_clk_src.c.fmax[VDD_DIG_LOW] = 274290000; + csi0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 320000000; csi1_clk_src.freq_tbl = ftbl_csi_clk_src_vq; - csi1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi1_clk_src.c.fmax[VDD_DIG_LOW] = 274290000; + csi1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 320000000; csi2_clk_src.freq_tbl = ftbl_csi_clk_src_vq; - csi2_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi2_clk_src.c.fmax[VDD_DIG_LOW] = 274290000; + csi2_clk_src.c.fmax[VDD_DIG_LOW_L1] = 320000000; csi3_clk_src.freq_tbl = ftbl_csi_clk_src_vq; - csi3_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi3_clk_src.c.fmax[VDD_DIG_LOW] = 274290000; + csi3_clk_src.c.fmax[VDD_DIG_LOW_L1] = 320000000; cpp_clk_src.freq_tbl = ftbl_cpp_clk_src_vq; - cpp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000; + cpp_clk_src.c.fmax[VDD_DIG_LOW] = 384000000; + cpp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 404000000; jpeg0_clk_src.freq_tbl = ftbl_jpeg0_clk_src_vq; jpeg0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 320000000; csiphy_clk_src.freq_tbl = ftbl_csiphy_clk_src_vq; + csiphy_clk_src.c.fmax[VDD_DIG_LOW] = 274290000; csiphy_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; fd_core_clk_src.freq_tbl = ftbl_fd_core_clk_src_vq; - fd_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 400000000; + fd_core_clk_src.c.fmax[VDD_DIG_LOW] = 404000000; + fd_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000; csi0phytimer_clk_src.c.fmax[VDD_DIG_LOW_L1] = 269333333; csi1phytimer_clk_src.c.fmax[VDD_DIG_LOW_L1] = 269333333; csi2phytimer_clk_src.c.fmax[VDD_DIG_LOW_L1] = 269333333; mdp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 330000000; + dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 154000000; extpclk_clk_src.c.fmax[VDD_DIG_LOW] = 312500000; extpclk_clk_src.c.fmax[VDD_DIG_LOW_L1] = 375000000; rot_clk_src.c.fmax[VDD_DIG_LOW_L1] = 330000000; - maxi_clk_src.freq_tbl = ftbl_maxi_clk_src_vq; video_core_clk_src.freq_tbl = ftbl_video_core_clk_src_vq; video_core_clk_src.c.fmax[VDD_DIG_LOWER] = 200000000; video_core_clk_src.c.fmax[VDD_DIG_LOW] = 269330000; - video_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 404000000; + video_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; video_core_clk_src.c.fmax[VDD_DIG_NOMINAL] = 444000000; video_core_clk_src.c.fmax[VDD_DIG_HIGH] = 533000000; video_subcore0_clk_src.freq_tbl = ftbl_video_subcore_clk_src_vq; video_subcore0_clk_src.c.fmax[VDD_DIG_LOWER] = 200000000; video_subcore0_clk_src.c.fmax[VDD_DIG_LOW] = 269330000; - video_subcore0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 404000000; + video_subcore0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; video_subcore0_clk_src.c.fmax[VDD_DIG_NOMINAL] = 444000000; video_subcore0_clk_src.c.fmax[VDD_DIG_HIGH] = 533000000; video_subcore1_clk_src.freq_tbl = ftbl_video_subcore_clk_src_vq; video_subcore1_clk_src.c.fmax[VDD_DIG_LOWER] = 200000000; video_subcore1_clk_src.c.fmax[VDD_DIG_LOW] = 269330000; - video_subcore1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 404000000; + video_subcore1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; video_subcore1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 444000000; video_subcore1_clk_src.c.fmax[VDD_DIG_HIGH] = 533000000; }; static void msm_mmsscc_v2_fixup(void) { - fd_core_clk_src.freq_tbl = ftbl_fd_core_clk_src_v2; - fd_core_clk_src.c.fmax[VDD_DIG_LOW] = 404000000; - fd_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000; + cpp_clk_src.c.fmax[VDD_DIG_LOW] = 200000000; + cpp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000; + csi0_clk_src.c.fmax[VDD_DIG_LOW] = 256000000; + csi0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi1_clk_src.c.fmax[VDD_DIG_LOW] = 256000000; + csi1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi2_clk_src.c.fmax[VDD_DIG_LOW] = 256000000; + csi2_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csi3_clk_src.c.fmax[VDD_DIG_LOW] = 256000000; + csi3_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000; + csiphy_clk_src.c.fmax[VDD_DIG_LOW] = 256000000; + + dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 148380000; + + video_subcore0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; + video_subcore1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; + video_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000; } int msm_mmsscc_cobalt_probe(struct platform_device *pdev) diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c index 1751f49b798c..19e813fd5a54 100644 --- a/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-cobalt.c @@ -53,6 +53,13 @@ #define PLL_FRAC_DIV_START_LOW_1 0x0d0 #define PLL_FRAC_DIV_START_MID_1 0x0d4 #define PLL_FRAC_DIV_START_HIGH_1 0x0d8 +#define PLL_SSC_STEPSIZE_LOW_1 0x10c +#define PLL_SSC_STEPSIZE_HIGH_1 0x110 +#define PLL_SSC_DIV_PER_LOW_1 0x114 +#define PLL_SSC_DIV_PER_HIGH_1 0x118 +#define PLL_SSC_DIV_ADJPER_LOW_1 0x11c +#define PLL_SSC_DIV_ADJPER_HIGH_1 0x120 +#define PLL_SSC_CONTROL 0x13c #define PLL_PLL_OUTDIV_RATE 0x140 #define PLL_PLL_LOCKDET_RATE_1 0x144 #define PLL_PLL_PROP_GAIN_RATE_1 0x14c @@ -70,6 +77,16 @@ #define PHY_CMN_PLL_CNTRL 0x038 #define PHY_CMN_CTRL_0 0x024 +/* Bit definition of SSC control registers */ +#define SSC_CENTER BIT(0) +#define SSC_EN BIT(1) +#define SSC_FREQ_UPDATE BIT(2) +#define SSC_FREQ_UPDATE_MUX BIT(3) +#define SSC_UPDATE_SSC BIT(4) +#define SSC_UPDATE_SSC_MUX BIT(5) +#define SSC_START BIT(6) +#define SSC_START_MUX BIT(7) + enum { DSI_PLL_0, DSI_PLL_1, @@ -84,6 +101,13 @@ struct dsi_pll_regs { u32 frac_div_start_low; u32 frac_div_start_mid; u32 frac_div_start_high; + u32 ssc_stepsize_low; + u32 ssc_stepsize_high; + u32 ssc_div_per_low; + u32 ssc_div_per_high; + u32 ssc_adjper_low; + u32 ssc_adjper_high; + u32 ssc_control; }; struct dsi_pll_config { @@ -92,6 +116,8 @@ struct dsi_pll_config { u32 output_div; bool ignore_frac; bool disable_prescaler; + bool enable_ssc; + bool ssc_center; u32 dec_bits; u32 frac_bits; u32 lock_timer; @@ -151,7 +177,7 @@ static void dsi_pll_setup_config(struct dsi_pll_cobalt *pll, config->frac_bits = 18; config->lock_timer = 64; config->ssc_freq = 31500; - config->ssc_offset = 4800; + config->ssc_offset = 5000; config->ssc_adj_per = 2; config->thresh_cycles = 32; config->refclk_cycles = 256; @@ -159,6 +185,15 @@ static void dsi_pll_setup_config(struct dsi_pll_cobalt *pll, config->div_override = false; config->ignore_frac = false; config->disable_prescaler = false; + config->enable_ssc = rsc->ssc_en; + config->ssc_center = rsc->ssc_center; + + if (config->enable_ssc) { + if (rsc->ssc_freq) + config->ssc_freq = rsc->ssc_freq; + if (rsc->ssc_ppm) + config->ssc_offset = rsc->ssc_ppm; + } dsi_pll_config_slave(rsc); } @@ -227,6 +262,77 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_cobalt *pll, regs->frac_div_start_high = (frac & 0x30000) >> 16; } +static void dsi_pll_calc_ssc(struct dsi_pll_cobalt *pll, + struct mdss_pll_resources *rsc) +{ + struct dsi_pll_config *config = &pll->pll_configuration; + struct dsi_pll_regs *regs = &pll->reg_setup; + u32 ssc_per; + u32 ssc_mod; + u64 ssc_step_size; + u64 frac; + + if (!config->enable_ssc) { + pr_debug("SSC not enabled\n"); + return; + } + + ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1; + ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1); + ssc_per -= ssc_mod; + + frac = regs->frac_div_start_low | + (regs->frac_div_start_mid << 8) | + (regs->frac_div_start_high << 16); + ssc_step_size = regs->decimal_div_start; + ssc_step_size *= (1 << config->frac_bits); + ssc_step_size += frac; + ssc_step_size *= config->ssc_offset; + ssc_step_size *= (config->ssc_adj_per + 1); + ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1)); + ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000); + + regs->ssc_div_per_low = ssc_per & 0xFF; + regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8; + regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF); + regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8); + regs->ssc_adjper_low = config->ssc_adj_per & 0xFF; + regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8; + + regs->ssc_control = config->ssc_center ? SSC_CENTER : 0; + + pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n", + regs->decimal_div_start, frac, config->frac_bits); + pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n", + ssc_per, (u32)ssc_step_size, config->ssc_adj_per); +} + +static void dsi_pll_ssc_commit(struct dsi_pll_cobalt *pll, + struct mdss_pll_resources *rsc) +{ + void __iomem *pll_base = rsc->pll_base; + struct dsi_pll_regs *regs = &pll->reg_setup; + + if (pll->pll_configuration.enable_ssc) { + pr_debug("SSC is enabled\n"); + + MDSS_PLL_REG_W(pll_base, PLL_SSC_STEPSIZE_LOW_1, + regs->ssc_stepsize_low); + MDSS_PLL_REG_W(pll_base, PLL_SSC_STEPSIZE_HIGH_1, + regs->ssc_stepsize_high); + MDSS_PLL_REG_W(pll_base, PLL_SSC_DIV_PER_LOW_1, + regs->ssc_div_per_low); + MDSS_PLL_REG_W(pll_base, PLL_SSC_DIV_PER_HIGH_1, + regs->ssc_div_per_high); + MDSS_PLL_REG_W(pll_base, PLL_SSC_DIV_ADJPER_LOW_1, + regs->ssc_adjper_low); + MDSS_PLL_REG_W(pll_base, PLL_SSC_DIV_ADJPER_HIGH_1, + regs->ssc_adjper_high); + MDSS_PLL_REG_W(pll_base, PLL_SSC_CONTROL, + SSC_EN | regs->ssc_control); + } +} + static void dsi_pll_config_hzindep_reg(struct dsi_pll_cobalt *pll, struct mdss_pll_resources *rsc) { @@ -270,8 +376,6 @@ static void dsi_pll_commit(struct dsi_pll_cobalt *pll, MDSS_PLL_REG_W(pll_base, PLL_PLL_OUTDIV_RATE, reg->pll_outdiv_rate); MDSS_PLL_REG_W(pll_base, PLL_PLL_LOCK_DELAY, 0x0a); - /* flush, ensure all register writes are done*/ - wmb(); } static int vco_cobalt_set_rate(struct clk *c, unsigned long rate) @@ -311,11 +415,16 @@ static int vco_cobalt_set_rate(struct clk *c, unsigned long rate) dsi_pll_calc_dec_frac(pll, rsc); + dsi_pll_calc_ssc(pll, rsc); + + dsi_pll_commit(pll, rsc); + dsi_pll_config_hzindep_reg(pll, rsc); - /* todo: ssc configuration */ + dsi_pll_ssc_commit(pll, rsc); - dsi_pll_commit(pll, rsc); + /* flush, ensure all register writes are done*/ + wmb(); mdss_pll_resource_enable(rsc, false); diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index c203ac7bfe8c..24005a1fda72 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -78,6 +78,16 @@ struct kgsl_dma_buf_meta { struct sg_table *table; }; +static inline struct kgsl_pagetable *_get_memdesc_pagetable( + struct kgsl_pagetable *pt, struct kgsl_mem_entry *entry) +{ + /* if a secured buffer, map it to secure global pagetable */ + if (kgsl_memdesc_is_secured(&entry->memdesc)) + return pt->mmu->securepagetable; + + return pt; +} + static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry); static const struct file_operations kgsl_fops; @@ -445,14 +455,17 @@ kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, /* map the memory after unlocking if gpuaddr has been assigned */ if (entry->memdesc.gpuaddr) { - /* if a secured buffer map it to secure global pagetable */ + pagetable = process->pagetable; if (kgsl_memdesc_is_secured(&entry->memdesc)) - pagetable = process->pagetable->mmu->securepagetable; - else - pagetable = process->pagetable; + pagetable = pagetable->mmu->securepagetable; entry->memdesc.pagetable = pagetable; - ret = kgsl_mmu_map(pagetable, &entry->memdesc); + + if (entry->memdesc.flags & KGSL_MEMFLAGS_SPARSE_VIRT) + ret = kgsl_mmu_sparse_dummy_map(pagetable, + &entry->memdesc, 0, entry->memdesc.size); + else if (entry->memdesc.gpuaddr) + ret = kgsl_mmu_map(pagetable, &entry->memdesc); if (ret) kgsl_mem_entry_detach_process(entry); } @@ -1270,6 +1283,24 @@ kgsl_sharedmem_find(struct kgsl_process_private *private, uint64_t gpuaddr) } EXPORT_SYMBOL(kgsl_sharedmem_find); +struct kgsl_mem_entry * __must_check +kgsl_sharedmem_find_id_flags(struct kgsl_process_private *process, + unsigned int id, uint64_t flags) +{ + int count = 0; + struct kgsl_mem_entry *entry; + + spin_lock(&process->mem_lock); + entry = idr_find(&process->mem_idr, id); + if (entry) + if (!entry->pending_free && + (flags & entry->memdesc.flags) == flags) + count = kgsl_mem_entry_get(entry); + spin_unlock(&process->mem_lock); + + return (count == 0) ? NULL : entry; +} + /** * kgsl_sharedmem_find_id() - find a memory entry by id * @process: the owning process @@ -1283,19 +1314,7 @@ EXPORT_SYMBOL(kgsl_sharedmem_find); struct kgsl_mem_entry * __must_check kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id) { - int result; - struct kgsl_mem_entry *entry; - - drain_workqueue(kgsl_driver.mem_workqueue); - - spin_lock(&process->mem_lock); - entry = idr_find(&process->mem_idr, id); - result = kgsl_mem_entry_get(entry); - spin_unlock(&process->mem_lock); - - if (result == 0) - return NULL; - return entry; + return kgsl_sharedmem_find_id_flags(process, id, 0); } /** @@ -3121,6 +3140,546 @@ long kgsl_ioctl_gpumem_get_info(struct kgsl_device_private *dev_priv, return result; } +static inline int _sparse_alloc_param_sanity_check(uint64_t size, + uint64_t pagesize) +{ + if (size == 0 || pagesize == 0) + return -EINVAL; + + if (pagesize != PAGE_SIZE && pagesize != SZ_64K) + return -EINVAL; + + if (pagesize > size || !IS_ALIGNED(size, pagesize)) + return -EINVAL; + + return 0; +} + +long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *process = dev_priv->process_priv; + struct kgsl_sparse_phys_alloc *param = data; + struct kgsl_mem_entry *entry; + int ret; + int id; + + ret = _sparse_alloc_param_sanity_check(param->size, param->pagesize); + if (ret) + return ret; + + entry = kgsl_mem_entry_create(); + if (entry == NULL) + return -ENOMEM; + + ret = kgsl_process_private_get(process); + if (!ret) { + ret = -EBADF; + goto err_free_entry; + } + + idr_preload(GFP_KERNEL); + spin_lock(&process->mem_lock); + /* Allocate the ID but don't attach the pointer just yet */ + id = idr_alloc(&process->mem_idr, NULL, 1, 0, GFP_NOWAIT); + spin_unlock(&process->mem_lock); + idr_preload_end(); + + if (id < 0) { + ret = id; + goto err_put_proc_priv; + } + + entry->id = id; + entry->priv = process; + + entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_PHYS; + kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize)); + + ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc, + process->pagetable, param->size, entry->memdesc.flags); + if (ret) + goto err_remove_idr; + + /* Sanity check to verify we got correct pagesize */ + if (param->pagesize != PAGE_SIZE && entry->memdesc.sgt != NULL) { + struct scatterlist *s; + int i; + + for_each_sg(entry->memdesc.sgt->sgl, s, + entry->memdesc.sgt->nents, i) { + if (!IS_ALIGNED(s->length, param->pagesize)) + goto err_invalid_pages; + } + } + + param->id = entry->id; + param->flags = entry->memdesc.flags; + + trace_sparse_phys_alloc(entry->id, param->size, param->pagesize); + kgsl_mem_entry_commit_process(entry); + + return 0; + +err_invalid_pages: + kgsl_sharedmem_free(&entry->memdesc); +err_remove_idr: + spin_lock(&process->mem_lock); + idr_remove(&process->mem_idr, entry->id); + spin_unlock(&process->mem_lock); +err_put_proc_priv: + kgsl_process_private_put(process); +err_free_entry: + kfree(entry); + + return ret; +} + +long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *process = dev_priv->process_priv; + struct kgsl_sparse_phys_free *param = data; + struct kgsl_mem_entry *entry; + + entry = kgsl_sharedmem_find_id_flags(process, param->id, + KGSL_MEMFLAGS_SPARSE_PHYS); + if (entry == NULL) + return -EINVAL; + + if (entry->memdesc.cur_bindings != 0) { + kgsl_mem_entry_put(entry); + return -EINVAL; + } + + trace_sparse_phys_free(entry->id); + + /* One put for find_id(), one put for the kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + kgsl_mem_entry_put(entry); + + return 0; +} + +long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_sparse_virt_alloc *param = data; + struct kgsl_mem_entry *entry; + int ret; + + ret = _sparse_alloc_param_sanity_check(param->size, param->pagesize); + if (ret) + return ret; + + entry = kgsl_mem_entry_create(); + if (entry == NULL) + return -ENOMEM; + + entry->memdesc.flags = KGSL_MEMFLAGS_SPARSE_VIRT; + entry->memdesc.size = param->size; + entry->memdesc.cur_bindings = 0; + kgsl_memdesc_set_align(&entry->memdesc, ilog2(param->pagesize)); + + spin_lock_init(&entry->bind_lock); + entry->bind_tree = RB_ROOT; + + ret = kgsl_mem_entry_attach_process(entry, dev_priv); + if (ret) { + kfree(entry); + return ret; + } + + param->id = entry->id; + param->gpuaddr = entry->memdesc.gpuaddr; + param->flags = entry->memdesc.flags; + + trace_sparse_virt_alloc(entry->id, param->size, param->pagesize); + kgsl_mem_entry_commit_process(entry); + + return 0; +} + +long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *process = dev_priv->process_priv; + struct kgsl_sparse_virt_free *param = data; + struct kgsl_mem_entry *entry = NULL; + + entry = kgsl_sharedmem_find_id_flags(process, param->id, + KGSL_MEMFLAGS_SPARSE_VIRT); + if (entry == NULL) + return -EINVAL; + + if (entry->bind_tree.rb_node != NULL) { + kgsl_mem_entry_put(entry); + return -EINVAL; + } + + trace_sparse_virt_free(entry->id); + + /* One put for find_id(), one put for the kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + kgsl_mem_entry_put(entry); + + return 0; +} + +static int _sparse_add_to_bind_tree(struct kgsl_mem_entry *entry, + uint64_t v_offset, + struct kgsl_memdesc *memdesc, + uint64_t p_offset, + uint64_t size, + uint64_t flags) +{ + struct sparse_bind_object *new; + struct rb_node **node, *parent = NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new == NULL) + return -ENOMEM; + + new->v_off = v_offset; + new->p_off = p_offset; + new->p_memdesc = memdesc; + new->size = size; + new->flags = flags; + + node = &entry->bind_tree.rb_node; + + while (*node != NULL) { + struct sparse_bind_object *this; + + parent = *node; + this = rb_entry(parent, struct sparse_bind_object, node); + + if (new->v_off < this->v_off) + node = &parent->rb_left; + else if (new->v_off > this->v_off) + node = &parent->rb_right; + } + + rb_link_node(&new->node, parent, node); + rb_insert_color(&new->node, &entry->bind_tree); + + return 0; +} + +static int _sparse_rm_from_bind_tree(struct kgsl_mem_entry *entry, + struct sparse_bind_object *obj, + uint64_t v_offset, uint64_t size) +{ + spin_lock(&entry->bind_lock); + if (v_offset == obj->v_off && size >= obj->size) { + /* + * We are all encompassing, remove the entry and free + * things up + */ + rb_erase(&obj->node, &entry->bind_tree); + kfree(obj); + } else if (v_offset == obj->v_off) { + /* + * We are the front of the node, adjust the front of + * the node + */ + obj->v_off += size; + obj->p_off += size; + obj->size -= size; + } else if ((v_offset + size) == (obj->v_off + obj->size)) { + /* + * We are at the end of the obj, adjust the beginning + * points + */ + obj->size -= size; + } else { + /* + * We are in the middle of a node, split it up and + * create a new mini node. Adjust this node's bounds + * and add the new node to the list. + */ + uint64_t tmp_size = obj->size; + int ret; + + obj->size = v_offset - obj->v_off; + + spin_unlock(&entry->bind_lock); + ret = _sparse_add_to_bind_tree(entry, v_offset + size, + obj->p_memdesc, + obj->p_off + (v_offset - obj->v_off) + size, + tmp_size - (v_offset - obj->v_off) - size, + obj->flags); + + return ret; + } + + spin_unlock(&entry->bind_lock); + + return 0; +} + +static struct sparse_bind_object *_find_containing_bind_obj( + struct kgsl_mem_entry *entry, + uint64_t offset, uint64_t size) +{ + struct sparse_bind_object *obj = NULL; + struct rb_node *node = entry->bind_tree.rb_node; + + spin_lock(&entry->bind_lock); + + while (node != NULL) { + obj = rb_entry(node, struct sparse_bind_object, node); + + if (offset == obj->v_off) { + break; + } else if (offset < obj->v_off) { + if (offset + size > obj->v_off) + break; + node = node->rb_left; + obj = NULL; + } else if (offset > obj->v_off) { + if (offset < obj->v_off + obj->size) + break; + node = node->rb_right; + obj = NULL; + } + } + + spin_unlock(&entry->bind_lock); + + return obj; +} + +static int _sparse_unbind(struct kgsl_mem_entry *entry, + struct sparse_bind_object *bind_obj, + uint64_t offset, uint64_t size) +{ + struct kgsl_memdesc *memdesc = bind_obj->p_memdesc; + struct kgsl_pagetable *pt = memdesc->pagetable; + int ret; + + if (memdesc->cur_bindings < (size / PAGE_SIZE)) + return -EINVAL; + + memdesc->cur_bindings -= size / PAGE_SIZE; + + ret = kgsl_mmu_unmap_offset(pt, memdesc, + entry->memdesc.gpuaddr, offset, size); + if (ret) + return ret; + + ret = kgsl_mmu_sparse_dummy_map(pt, &entry->memdesc, offset, size); + if (ret) + return ret; + + ret = _sparse_rm_from_bind_tree(entry, bind_obj, offset, size); + if (ret == 0) { + atomic_long_sub(size, &kgsl_driver.stats.mapped); + trace_sparse_unbind(entry->id, offset, size); + } + + return ret; +} + +static long sparse_unbind_range(struct kgsl_sparse_binding_object *obj, + struct kgsl_mem_entry *virt_entry) +{ + struct sparse_bind_object *bind_obj; + int ret = 0; + uint64_t size = obj->size; + uint64_t tmp_size = obj->size; + uint64_t offset = obj->virtoffset; + + while (size > 0 && ret == 0) { + tmp_size = size; + bind_obj = _find_containing_bind_obj(virt_entry, offset, size); + if (bind_obj == NULL) + return 0; + + if (bind_obj->v_off > offset) { + tmp_size = size - bind_obj->v_off - offset; + if (tmp_size > bind_obj->size) + tmp_size = bind_obj->size; + offset = bind_obj->v_off; + } else if (bind_obj->v_off < offset) { + uint64_t diff = offset - bind_obj->v_off; + + if (diff + size > bind_obj->size) + tmp_size = bind_obj->size - diff; + } else { + if (tmp_size > bind_obj->size) + tmp_size = bind_obj->size; + } + + ret = _sparse_unbind(virt_entry, bind_obj, offset, tmp_size); + if (ret == 0) { + offset += tmp_size; + size -= tmp_size; + } + } + + return ret; +} + +static inline bool _is_phys_bindable(struct kgsl_mem_entry *phys_entry, + uint64_t offset, uint64_t size, uint64_t flags) +{ + struct kgsl_memdesc *memdesc = &phys_entry->memdesc; + + if (!IS_ALIGNED(offset | size, kgsl_memdesc_get_pagesize(memdesc))) + return false; + + if (!(flags & KGSL_SPARSE_BIND_MULTIPLE_TO_PHYS) && + offset + size > memdesc->size) + return false; + + return true; +} + +static int _sparse_bind(struct kgsl_process_private *process, + struct kgsl_mem_entry *virt_entry, uint64_t v_offset, + struct kgsl_mem_entry *phys_entry, uint64_t p_offset, + uint64_t size, uint64_t flags) +{ + int ret; + struct kgsl_pagetable *pagetable; + struct kgsl_memdesc *memdesc = &phys_entry->memdesc; + + /* map the memory after unlocking if gpuaddr has been assigned */ + if (memdesc->gpuaddr) + return -EINVAL; + + if (memdesc->useraddr != 0) + return -EINVAL; + + pagetable = memdesc->pagetable; + + /* Clear out any mappings */ + ret = kgsl_mmu_unmap_offset(pagetable, &virt_entry->memdesc, + virt_entry->memdesc.gpuaddr, v_offset, size); + if (ret) + return ret; + + ret = kgsl_mmu_map_offset(pagetable, virt_entry->memdesc.gpuaddr, + v_offset, memdesc, p_offset, size, flags); + if (ret) { + /* Try to clean up, but not the end of the world */ + kgsl_mmu_sparse_dummy_map(pagetable, &virt_entry->memdesc, + v_offset, size); + return ret; + } + + ret = _sparse_add_to_bind_tree(virt_entry, v_offset, memdesc, + p_offset, size, flags); + if (ret == 0) + memdesc->cur_bindings += size / PAGE_SIZE; + + return ret; +} + +static long sparse_bind_range(struct kgsl_process_private *private, + struct kgsl_sparse_binding_object *obj, + struct kgsl_mem_entry *virt_entry) +{ + struct kgsl_mem_entry *phys_entry; + int ret; + + phys_entry = kgsl_sharedmem_find_id_flags(private, obj->id, + KGSL_MEMFLAGS_SPARSE_PHYS); + if (phys_entry == NULL) + return -EINVAL; + + if (!_is_phys_bindable(phys_entry, obj->physoffset, obj->size, + obj->flags)) { + kgsl_mem_entry_put(phys_entry); + return -EINVAL; + } + + if (kgsl_memdesc_get_align(&virt_entry->memdesc) != + kgsl_memdesc_get_align(&phys_entry->memdesc)) { + kgsl_mem_entry_put(phys_entry); + return -EINVAL; + } + + ret = sparse_unbind_range(obj, virt_entry); + if (ret) { + kgsl_mem_entry_put(phys_entry); + return -EINVAL; + } + + ret = _sparse_bind(private, virt_entry, obj->virtoffset, + phys_entry, obj->physoffset, obj->size, + obj->flags & KGSL_SPARSE_BIND_MULTIPLE_TO_PHYS); + if (ret == 0) { + KGSL_STATS_ADD(obj->size, &kgsl_driver.stats.mapped, + &kgsl_driver.stats.mapped_max); + + trace_sparse_bind(virt_entry->id, obj->virtoffset, + phys_entry->id, obj->physoffset, + obj->size, obj->flags); + } + + kgsl_mem_entry_put(phys_entry); + + return ret; +} + +long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_sparse_bind *param = data; + struct kgsl_sparse_binding_object obj; + struct kgsl_mem_entry *virt_entry; + int pg_sz; + void __user *ptr; + int ret = 0; + int i = 0; + + ptr = (void __user *) (uintptr_t) param->list; + + if (param->size > sizeof(struct kgsl_sparse_binding_object) || + param->count == 0 || ptr == NULL) + return -EINVAL; + + virt_entry = kgsl_sharedmem_find_id_flags(private, param->id, + KGSL_MEMFLAGS_SPARSE_VIRT); + if (virt_entry == NULL) + return -EINVAL; + + pg_sz = kgsl_memdesc_get_pagesize(&virt_entry->memdesc); + + for (i = 0; i < param->count; i++) { + memset(&obj, 0, sizeof(obj)); + ret = _copy_from_user(&obj, ptr, sizeof(obj), param->size); + if (ret) + break; + + /* Sanity check initial range */ + if (obj.size == 0 || + obj.virtoffset + obj.size > virt_entry->memdesc.size || + !(IS_ALIGNED(obj.virtoffset | obj.size, pg_sz))) { + ret = -EINVAL; + break; + } + + if (obj.flags & KGSL_SPARSE_BIND) + ret = sparse_bind_range(private, &obj, virt_entry); + else if (obj.flags & KGSL_SPARSE_UNBIND) + ret = sparse_unbind_range(&obj, virt_entry); + else + ret = -EINVAL; + if (ret) + break; + + ptr += sizeof(obj); + } + + kgsl_mem_entry_put(virt_entry); + + return ret; +} + long kgsl_ioctl_gpuobj_info(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { @@ -3356,6 +3915,13 @@ get_mmap_entry(struct kgsl_process_private *private, goto err_put; } + if (entry->memdesc.flags & KGSL_MEMFLAGS_SPARSE_PHYS) { + if (len != entry->memdesc.size) { + ret = -EINVAL; + goto err_put; + } + } + if (entry->memdesc.useraddr != 0) { ret = -EBUSY; goto err_put; diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index ee7149e1fd41..25f5de6ce645 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -184,6 +184,7 @@ struct kgsl_memdesc_ops { * @attrs: dma attributes for this memory * @pages: An array of pointers to allocated pages * @page_count: Total number of pages allocated + * @cur_bindings: Number of sparse pages actively bound */ struct kgsl_memdesc { struct kgsl_pagetable *pagetable; @@ -202,6 +203,7 @@ struct kgsl_memdesc { struct dma_attrs attrs; struct page **pages; unsigned int page_count; + unsigned int cur_bindings; }; /* @@ -235,6 +237,8 @@ struct kgsl_memdesc { * @dev_priv: back pointer to the device file that created this entry. * @metadata: String containing user specified metadata for the entry * @work: Work struct used to schedule a kgsl_mem_entry_put in atomic contexts + * @bind_lock: Lock for sparse memory bindings + * @bind_tree: RB Tree for sparse memory bindings */ struct kgsl_mem_entry { struct kref refcount; @@ -246,6 +250,8 @@ struct kgsl_mem_entry { int pending_free; char metadata[KGSL_GPUOBJ_ALLOC_METADATA_MAX + 1]; struct work_struct work; + spinlock_t bind_lock; + struct rb_root bind_tree; }; struct kgsl_device_private; @@ -315,6 +321,24 @@ struct kgsl_protected_registers { int range; }; +/** + * struct sparse_bind_object - Bind metadata + * @node: Node for the rb tree + * @p_memdesc: Physical memdesc bound to + * @v_off: Offset of bind in the virtual entry + * @p_off: Offset of bind in the physical memdesc + * @size: Size of the bind + * @flags: Flags for the bind + */ +struct sparse_bind_object { + struct rb_node node; + struct kgsl_memdesc *p_memdesc; + uint64_t v_off; + uint64_t p_off; + uint64_t size; + uint64_t flags; +}; + long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data); long kgsl_ioctl_device_setproperty(struct kgsl_device_private *dev_priv, @@ -377,6 +401,19 @@ long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv, long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data); +long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); +long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); +long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); +long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); +long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); +long kgsl_ioctl_sparse_unbind(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); + void kgsl_mem_entry_destroy(struct kref *kref); struct kgsl_mem_entry * __must_check diff --git a/drivers/gpu/msm/kgsl_compat.c b/drivers/gpu/msm/kgsl_compat.c index 248c78b7e5c4..028a9566fa14 100644 --- a/drivers/gpu/msm/kgsl_compat.c +++ b/drivers/gpu/msm/kgsl_compat.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, 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 @@ -372,6 +372,16 @@ static const struct kgsl_ioctl kgsl_compat_ioctl_funcs[] = { kgsl_ioctl_gpu_command), KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUOBJ_SET_INFO, kgsl_ioctl_gpuobj_set_info), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_PHYS_ALLOC, + kgsl_ioctl_sparse_phys_alloc), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_PHYS_FREE, + kgsl_ioctl_sparse_phys_free), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_VIRT_ALLOC, + kgsl_ioctl_sparse_virt_alloc), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_VIRT_FREE, + kgsl_ioctl_sparse_virt_free), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND, + kgsl_ioctl_sparse_bind), }; long kgsl_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index 93ac790f3a55..df9eb9ebd779 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -129,10 +129,13 @@ static int print_mem_entry(int id, void *ptr, void *data) { struct seq_file *s = data; struct kgsl_mem_entry *entry = ptr; - char flags[9]; + char flags[10]; char usage[16]; struct kgsl_memdesc *m = &entry->memdesc; + if (m->flags & KGSL_MEMFLAGS_SPARSE_VIRT) + return 0; + flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-'; flags[1] = '-'; flags[2] = !(m->flags & KGSL_MEMFLAGS_GPUREADONLY) ? 'w' : '-'; @@ -141,7 +144,8 @@ static int print_mem_entry(int id, void *ptr, void *data) flags[5] = kgsl_memdesc_use_cpu_map(m) ? 'p' : '-'; flags[6] = (m->useraddr) ? 'Y' : 'N'; flags[7] = kgsl_memdesc_is_secured(m) ? 's' : '-'; - flags[8] = '\0'; + flags[8] = m->flags & KGSL_MEMFLAGS_SPARSE_PHYS ? 'P' : '-'; + flags[9] = '\0'; kgsl_get_memory_usage(usage, sizeof(usage), m->flags); @@ -211,6 +215,70 @@ static const struct file_operations process_mem_fops = { .release = process_mem_release, }; +static int print_sparse_mem_entry(int id, void *ptr, void *data) +{ + struct seq_file *s = data; + struct kgsl_mem_entry *entry = ptr; + struct kgsl_memdesc *m = &entry->memdesc; + struct rb_node *node; + + if (!(m->flags & KGSL_MEMFLAGS_SPARSE_VIRT)) + return 0; + + node = rb_first(&entry->bind_tree); + + while (node != NULL) { + struct sparse_bind_object *obj = rb_entry(node, + struct sparse_bind_object, node); + seq_printf(s, "%5d %16llx %16llx %16llx %16llx\n", + entry->id, entry->memdesc.gpuaddr, + obj->v_off, obj->size, obj->p_off); + node = rb_next(node); + } + + seq_putc(s, '\n'); + + return 0; +} + +static int process_sparse_mem_print(struct seq_file *s, void *unused) +{ + struct kgsl_process_private *private = s->private; + + seq_printf(s, "%5s %16s %16s %16s %16s\n", + "v_id", "gpuaddr", "v_offset", "v_size", "p_offset"); + + spin_lock(&private->mem_lock); + idr_for_each(&private->mem_idr, print_sparse_mem_entry, s); + spin_unlock(&private->mem_lock); + + return 0; +} + +static int process_sparse_mem_open(struct inode *inode, struct file *file) +{ + int ret; + pid_t pid = (pid_t) (unsigned long) inode->i_private; + struct kgsl_process_private *private = NULL; + + private = kgsl_process_private_find(pid); + + if (!private) + return -ENODEV; + + ret = single_open(file, process_sparse_mem_print, private); + if (ret) + kgsl_process_private_put(private); + + return ret; +} + +static const struct file_operations process_sparse_mem_fops = { + .open = process_sparse_mem_open, + .read = seq_read, + .llseek = seq_lseek, + .release = process_mem_release, +}; /** * kgsl_process_init_debugfs() - Initialize debugfs for a process @@ -251,6 +319,15 @@ void kgsl_process_init_debugfs(struct kgsl_process_private *private) if (IS_ERR_OR_NULL(dentry)) WARN((dentry == NULL), "Unable to create 'mem' file for %s\n", name); + + dentry = debugfs_create_file("sparse_mem", 0444, private->debug_root, + (void *) ((unsigned long) private->pid), + &process_sparse_mem_fops); + + if (IS_ERR_OR_NULL(dentry)) + WARN((dentry == NULL), + "Unable to create 'sparse_mem' file for %s\n", name); + } void kgsl_core_debugfs_init(void) diff --git a/drivers/gpu/msm/kgsl_ioctl.c b/drivers/gpu/msm/kgsl_ioctl.c index 0802e94f56ad..894e6a4a146b 100644 --- a/drivers/gpu/msm/kgsl_ioctl.c +++ b/drivers/gpu/msm/kgsl_ioctl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2016, 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 @@ -90,6 +90,16 @@ static const struct kgsl_ioctl kgsl_ioctl_funcs[] = { kgsl_ioctl_gpu_command), KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUOBJ_SET_INFO, kgsl_ioctl_gpuobj_set_info), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_PHYS_ALLOC, + kgsl_ioctl_sparse_phys_alloc), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_PHYS_FREE, + kgsl_ioctl_sparse_phys_free), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_VIRT_ALLOC, + kgsl_ioctl_sparse_virt_alloc), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_VIRT_FREE, + kgsl_ioctl_sparse_virt_free), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND, + kgsl_ioctl_sparse_bind), }; long kgsl_ioctl_copy_in(unsigned int kernel_cmd, unsigned int user_cmd, diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index b467ef81d257..166bb68e64a1 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -323,8 +323,8 @@ static int _iommu_map_sync_pc(struct kgsl_pagetable *pt, _unlock_if_secure_mmu(memdesc, pt->mmu); if (ret) { - KGSL_CORE_ERR("map err: %p, 0x%016llX, 0x%llx, 0x%x, %d\n", - iommu_pt->domain, gpuaddr, size, flags, ret); + KGSL_CORE_ERR("map err: 0x%016llX, 0x%llx, 0x%x, %d\n", + gpuaddr, size, flags, ret); return -ENODEV; } @@ -351,8 +351,8 @@ static int _iommu_unmap_sync_pc(struct kgsl_pagetable *pt, _unlock_if_secure_mmu(memdesc, pt->mmu); if (unmapped != size) { - KGSL_CORE_ERR("unmap err: %p, 0x%016llx, 0x%llx, %zd\n", - iommu_pt->domain, addr, size, unmapped); + KGSL_CORE_ERR("unmap err: 0x%016llx, 0x%llx, %zd\n", + addr, size, unmapped); return -ENODEV; } @@ -421,8 +421,9 @@ static int _iommu_map_sg_offset_sync_pc(struct kgsl_pagetable *pt, if (size != 0) { /* Cleanup on error */ _iommu_unmap_sync_pc(pt, memdesc, addr, mapped); - KGSL_CORE_ERR("map err: %p, 0x%016llX, %d, %x, %zd\n", - iommu_pt->domain, addr, nents, flags, mapped); + KGSL_CORE_ERR( + "map sg offset err: 0x%016llX, %d, %x, %zd\n", + addr, nents, flags, mapped); return -ENODEV; } @@ -451,8 +452,8 @@ static int _iommu_map_sg_sync_pc(struct kgsl_pagetable *pt, _unlock_if_secure_mmu(memdesc, pt->mmu); if (mapped == 0) { - KGSL_CORE_ERR("map err: %p, 0x%016llX, %d, %x, %zd\n", - iommu_pt->domain, addr, nents, flags, mapped); + KGSL_CORE_ERR("map sg err: 0x%016llX, %d, %x, %zd\n", + addr, nents, flags, mapped); return -ENODEV; } @@ -467,6 +468,13 @@ static int _iommu_map_sg_sync_pc(struct kgsl_pagetable *pt, static struct page *kgsl_guard_page; static struct kgsl_memdesc kgsl_secure_guard_page_memdesc; +/* + * The dummy page is a placeholder/extra page to be used for sparse mappings. + * This page will be mapped to all virtual sparse bindings that are not + * physically backed. + */ +static struct page *kgsl_dummy_page; + /* These functions help find the nearest allocated memory entries on either side * of a faulting address. If we know the nearby allocations memory we can * get a better determination of what we think should have been located in the @@ -1309,6 +1317,11 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu) kgsl_guard_page = NULL; } + if (kgsl_dummy_page != NULL) { + __free_page(kgsl_dummy_page); + kgsl_dummy_page = NULL; + } + kgsl_iommu_remove_global(mmu, &iommu->setstate); kgsl_sharedmem_free(&iommu->setstate); kgsl_cleanup_qdss_desc(mmu); @@ -1523,6 +1536,8 @@ kgsl_iommu_unmap_offset(struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset, uint64_t size) { + if (size == 0 || (size + offset) > kgsl_memdesc_footprint(memdesc)) + return -EINVAL; /* * All GPU addresses as assigned are page aligned, but some * functions perturb the gpuaddr with an offset, so apply the @@ -1530,9 +1545,8 @@ kgsl_iommu_unmap_offset(struct kgsl_pagetable *pt, */ addr = PAGE_ALIGN(addr); - - if (size == 0 || addr == 0) - return 0; + if (addr == 0) + return -EINVAL; return _iommu_unmap_sync_pc(pt, memdesc, addr + offset, size); } @@ -1540,13 +1554,11 @@ kgsl_iommu_unmap_offset(struct kgsl_pagetable *pt, static int kgsl_iommu_unmap(struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc) { - uint64_t size = memdesc->size; - - if (kgsl_memdesc_has_guard_page(memdesc)) - size += kgsl_memdesc_guard_page_size(pt->mmu, memdesc); + if (memdesc->size == 0 || memdesc->gpuaddr == 0) + return -EINVAL; return kgsl_iommu_unmap_offset(pt, memdesc, memdesc->gpuaddr, 0, - size); + kgsl_memdesc_footprint(memdesc)); } /** @@ -1593,7 +1605,7 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt, } else { if (kgsl_guard_page == NULL) { kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO | - __GFP_HIGHMEM); + __GFP_NORETRY | __GFP_HIGHMEM); if (kgsl_guard_page == NULL) return -ENOMEM; } @@ -1602,7 +1614,7 @@ static int _iommu_map_guard_page(struct kgsl_pagetable *pt, } return _iommu_map_sync_pc(pt, memdesc, gpuaddr, physaddr, - kgsl_memdesc_guard_page_size(pt->mmu, memdesc), + kgsl_memdesc_guard_page_size(memdesc), protflags & ~IOMMU_WRITE); } @@ -1658,6 +1670,100 @@ done: return ret; } +static int kgsl_iommu_sparse_dummy_map(struct kgsl_pagetable *pt, + struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size) +{ + int ret = 0, i; + struct page **pages = NULL; + struct sg_table sgt; + int count = size >> PAGE_SHIFT; + + /* verify the offset is within our range */ + if (size + offset > memdesc->size) + return -EINVAL; + + if (kgsl_dummy_page == NULL) { + kgsl_dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_HIGHMEM); + if (kgsl_dummy_page == NULL) + return -ENOMEM; + } + + pages = kcalloc(count, sizeof(struct page *), GFP_KERNEL); + if (pages == NULL) + return -ENOMEM; + + for (i = 0; i < count; i++) + pages[i] = kgsl_dummy_page; + + ret = sg_alloc_table_from_pages(&sgt, pages, count, + 0, size, GFP_KERNEL); + if (ret == 0) { + ret = _iommu_map_sg_sync_pc(pt, memdesc->gpuaddr + offset, + memdesc, sgt.sgl, sgt.nents, + IOMMU_READ | IOMMU_NOEXEC); + sg_free_table(&sgt); + } + + kfree(pages); + + return ret; +} + +static int _map_to_one_page(struct kgsl_pagetable *pt, uint64_t addr, + struct kgsl_memdesc *memdesc, uint64_t physoffset, + uint64_t size, unsigned int map_flags) +{ + int ret = 0, i; + int pg_sz = kgsl_memdesc_get_pagesize(memdesc); + int count = size >> PAGE_SHIFT; + struct page *page = NULL; + struct page **pages = NULL; + struct sg_page_iter sg_iter; + struct sg_table sgt; + + /* Find our physaddr offset addr */ + if (memdesc->pages != NULL) + page = memdesc->pages[physoffset >> PAGE_SHIFT]; + else { + for_each_sg_page(memdesc->sgt->sgl, &sg_iter, + memdesc->sgt->nents, physoffset >> PAGE_SHIFT) { + page = sg_page_iter_page(&sg_iter); + break; + } + } + + if (page == NULL) + return -EINVAL; + + pages = kcalloc(count, sizeof(struct page *), GFP_KERNEL); + if (pages == NULL) + return -ENOMEM; + + for (i = 0; i < count; i++) { + if (pg_sz != PAGE_SIZE) { + struct page *tmp_page = page; + int j; + + for (j = 0; j < 16; j++, tmp_page += PAGE_SIZE) + pages[i++] = tmp_page; + } else + pages[i] = page; + } + + ret = sg_alloc_table_from_pages(&sgt, pages, count, + 0, size, GFP_KERNEL); + if (ret == 0) { + ret = _iommu_map_sg_sync_pc(pt, addr, memdesc, sgt.sgl, + sgt.nents, map_flags); + sg_free_table(&sgt); + } + + kfree(pages); + + return ret; +} + static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt, uint64_t virtaddr, uint64_t virtoffset, struct kgsl_memdesc *memdesc, uint64_t physoffset, @@ -1668,13 +1774,17 @@ static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt, int ret; struct sg_table *sgt = NULL; - pg_sz = (1 << kgsl_memdesc_get_align(memdesc)); + pg_sz = kgsl_memdesc_get_pagesize(memdesc); if (!IS_ALIGNED(virtaddr | virtoffset | physoffset | size, pg_sz)) return -EINVAL; if (size == 0) return -EINVAL; + if (!(feature_flag & KGSL_SPARSE_BIND_MULTIPLE_TO_PHYS) && + size + physoffset > kgsl_memdesc_footprint(memdesc)) + return -EINVAL; + /* * For paged memory allocated through kgsl, memdesc->pages is not NULL. * Allocate sgt here just for its map operation. Contiguous memory @@ -1688,9 +1798,13 @@ static int kgsl_iommu_map_offset(struct kgsl_pagetable *pt, if (IS_ERR(sgt)) return PTR_ERR(sgt); - ret = _iommu_map_sg_offset_sync_pc(pt, virtaddr + virtoffset, - memdesc, sgt->sgl, sgt->nents, - physoffset, size, protflags); + if (feature_flag & KGSL_SPARSE_BIND_MULTIPLE_TO_PHYS) + ret = _map_to_one_page(pt, virtaddr + virtoffset, + memdesc, physoffset, size, protflags); + else + ret = _iommu_map_sg_offset_sync_pc(pt, virtaddr + virtoffset, + memdesc, sgt->sgl, sgt->nents, + physoffset, size, protflags); if (memdesc->pages != NULL) kgsl_free_sgt(sgt); @@ -2152,8 +2266,7 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable, { struct kgsl_iommu_pt *pt = pagetable->priv; int ret = 0; - uint64_t addr, start, end; - uint64_t size = memdesc->size; + uint64_t addr, start, end, size; unsigned int align; BUG_ON(kgsl_memdesc_use_cpu_map(memdesc)); @@ -2162,8 +2275,7 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable, pagetable->name != KGSL_MMU_SECURE_PT) return -EINVAL; - if (kgsl_memdesc_has_guard_page(memdesc)) - size += kgsl_memdesc_guard_page_size(pagetable->mmu, memdesc); + size = kgsl_memdesc_footprint(memdesc); align = 1 << kgsl_memdesc_get_align(memdesc); @@ -2445,4 +2557,5 @@ static struct kgsl_mmu_pt_ops iommu_pt_ops = { .addr_in_range = kgsl_iommu_addr_in_range, .mmu_map_offset = kgsl_iommu_map_offset, .mmu_unmap_offset = kgsl_iommu_unmap_offset, + .mmu_sparse_dummy_map = kgsl_iommu_sparse_dummy_map, }; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 8b0d93fda32c..10f6b8049d36 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -386,29 +386,24 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc) { - int ret = 0; int size; if (!memdesc->gpuaddr) return -EINVAL; - /* Only global mappings should be mapped multiple times */ - if (!kgsl_memdesc_is_global(memdesc) && - (KGSL_MEMDESC_MAPPED & memdesc->priv)) - return -EINVAL; size = kgsl_memdesc_footprint(memdesc); - if (PT_OP_VALID(pagetable, mmu_map)) - ret = pagetable->pt_ops->mmu_map(pagetable, memdesc); - - if (ret) - return ret; + if (PT_OP_VALID(pagetable, mmu_map)) { + int ret; - atomic_inc(&pagetable->stats.entries); - KGSL_STATS_ADD(size, &pagetable->stats.mapped, - &pagetable->stats.max_mapped); + ret = pagetable->pt_ops->mmu_map(pagetable, memdesc); + if (ret) + return ret; - memdesc->priv |= KGSL_MEMDESC_MAPPED; + atomic_inc(&pagetable->stats.entries); + KGSL_STATS_ADD(size, &pagetable->stats.mapped, + &pagetable->stats.max_mapped); + } return 0; } @@ -455,22 +450,22 @@ int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc) { - uint64_t size; - - if (memdesc->size == 0 || memdesc->gpuaddr == 0 || - !(KGSL_MEMDESC_MAPPED & memdesc->priv)) + if (memdesc->size == 0) return -EINVAL; - size = kgsl_memdesc_footprint(memdesc); + if (PT_OP_VALID(pagetable, mmu_unmap)) { + int ret; + uint64_t size; - if (PT_OP_VALID(pagetable, mmu_unmap)) - pagetable->pt_ops->mmu_unmap(pagetable, memdesc); + size = kgsl_memdesc_footprint(memdesc); - atomic_dec(&pagetable->stats.entries); - atomic_long_sub(size, &pagetable->stats.mapped); + ret = pagetable->pt_ops->mmu_unmap(pagetable, memdesc); + if (ret) + return ret; - if (!kgsl_memdesc_is_global(memdesc)) - memdesc->priv &= ~KGSL_MEMDESC_MAPPED; + atomic_dec(&pagetable->stats.entries); + atomic_long_sub(size, &pagetable->stats.mapped); + } return 0; } @@ -481,11 +476,20 @@ int kgsl_mmu_map_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, uint64_t physoffset, uint64_t size, uint64_t flags) { - if (PT_OP_VALID(pagetable, mmu_map_offset)) - return pagetable->pt_ops->mmu_map_offset(pagetable, virtaddr, + if (PT_OP_VALID(pagetable, mmu_map_offset)) { + int ret; + + ret = pagetable->pt_ops->mmu_map_offset(pagetable, virtaddr, virtoffset, memdesc, physoffset, size, flags); + if (ret) + return ret; + + atomic_inc(&pagetable->stats.entries); + KGSL_STATS_ADD(size, &pagetable->stats.mapped, + &pagetable->stats.max_mapped); + } - return -EINVAL; + return 0; } EXPORT_SYMBOL(kgsl_mmu_map_offset); @@ -493,14 +497,41 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset, uint64_t size) { - if (PT_OP_VALID(pagetable, mmu_unmap_offset)) - return pagetable->pt_ops->mmu_unmap_offset(pagetable, memdesc, + if (PT_OP_VALID(pagetable, mmu_unmap_offset)) { + int ret; + + ret = pagetable->pt_ops->mmu_unmap_offset(pagetable, memdesc, addr, offset, size); + if (ret) + return ret; + + atomic_dec(&pagetable->stats.entries); + atomic_long_sub(size, &pagetable->stats.mapped); + } - return -EINVAL; + return 0; } EXPORT_SYMBOL(kgsl_mmu_unmap_offset); +int kgsl_mmu_sparse_dummy_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size) +{ + if (PT_OP_VALID(pagetable, mmu_sparse_dummy_map)) { + int ret; + + ret = pagetable->pt_ops->mmu_sparse_dummy_map(pagetable, + memdesc, offset, size); + if (ret) + return ret; + + atomic_dec(&pagetable->stats.entries); + atomic_long_sub(size, &pagetable->stats.mapped); + } + + return 0; +} +EXPORT_SYMBOL(kgsl_mmu_sparse_dummy_map); + void kgsl_mmu_remove_global(struct kgsl_device *device, struct kgsl_memdesc *memdesc) { diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 588777af353f..53645cc1741c 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -106,6 +106,9 @@ struct kgsl_mmu_pt_ops { int (*mmu_unmap_offset)(struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset, uint64_t size); + int (*mmu_sparse_dummy_map)(struct kgsl_pagetable *pt, + struct kgsl_memdesc *memdesc, uint64_t offset, + uint64_t size); }; /* @@ -230,6 +233,9 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device); +int kgsl_mmu_sparse_dummy_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size); + /* * Static inline functions of MMU that simply call the SMMU specific * function using a function pointer. These functions can be thought diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index c05aaecb5284..565ae4c39fdd 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -92,6 +92,18 @@ kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc) } /* + * kgsl_memdesc_get_pagesize - Get pagesize based on alignment + * @memdesc - the memdesc + * + * Returns the pagesize based on memdesc alignment + */ +static inline int +kgsl_memdesc_get_pagesize(const struct kgsl_memdesc *memdesc) +{ + return (1 << kgsl_memdesc_get_align(memdesc)); +} + +/* * kgsl_memdesc_get_cachemode - Get cache mode of a memdesc * @memdesc: the memdesc * @@ -211,12 +223,19 @@ kgsl_memdesc_has_guard_page(const struct kgsl_memdesc *memdesc) * * Returns guard page size */ -static inline int -kgsl_memdesc_guard_page_size(const struct kgsl_mmu *mmu, - const struct kgsl_memdesc *memdesc) +static inline uint64_t +kgsl_memdesc_guard_page_size(const struct kgsl_memdesc *memdesc) { - return kgsl_memdesc_is_secured(memdesc) ? mmu->secure_align_mask + 1 : - PAGE_SIZE; + if (!kgsl_memdesc_has_guard_page(memdesc)) + return 0; + + if (kgsl_memdesc_is_secured(memdesc)) { + if (memdesc->pagetable != NULL && + memdesc->pagetable->mmu != NULL) + return memdesc->pagetable->mmu->secure_align_mask + 1; + } + + return PAGE_SIZE; } /* @@ -241,10 +260,7 @@ kgsl_memdesc_use_cpu_map(const struct kgsl_memdesc *memdesc) static inline uint64_t kgsl_memdesc_footprint(const struct kgsl_memdesc *memdesc) { - uint64_t size = memdesc->size; - if (kgsl_memdesc_has_guard_page(memdesc)) - size += SZ_4K; - return size; + return memdesc->size + kgsl_memdesc_guard_page_size(memdesc); } /* diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 69ae2e3fd2d7..f9d3ede718ab 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -313,6 +313,13 @@ int kgsl_snapshot_get_object(struct kgsl_snapshot *snapshot, goto err_put; } + /* Do not save sparse memory */ + if (entry->memdesc.flags & KGSL_MEMFLAGS_SPARSE_VIRT || + entry->memdesc.flags & KGSL_MEMFLAGS_SPARSE_PHYS) { + ret = 0; + goto err_put; + } + /* * size indicates the number of bytes in the region to save. This might * not always be the entire size of the region because some buffers are diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index 8988dc12839f..bac09175cf12 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -1102,6 +1102,100 @@ TRACE_EVENT(kgsl_msg, ) ); +DECLARE_EVENT_CLASS(sparse_alloc_template, + TP_PROTO(unsigned int id, uint64_t size, unsigned int pagesize), + TP_ARGS(id, size, pagesize), + TP_STRUCT__entry( + __field(unsigned int, id) + __field(uint64_t, size) + __field(unsigned int, pagesize) + ), + TP_fast_assign( + __entry->id = id; + __entry->size = size; + __entry->pagesize = pagesize; + ), + TP_printk("id=%d size=0x%llX pagesize=0x%X", + __entry->id, __entry->size, __entry->pagesize) +); + +DEFINE_EVENT(sparse_alloc_template, sparse_phys_alloc, + TP_PROTO(unsigned int id, uint64_t size, unsigned int pagesize), + TP_ARGS(id, size, pagesize) +); + +DEFINE_EVENT(sparse_alloc_template, sparse_virt_alloc, + TP_PROTO(unsigned int id, uint64_t size, unsigned int pagesize), + TP_ARGS(id, size, pagesize) +); + +DECLARE_EVENT_CLASS(sparse_free_template, + TP_PROTO(unsigned int id), + TP_ARGS(id), + TP_STRUCT__entry( + __field(unsigned int, id) + ), + TP_fast_assign( + __entry->id = id; + ), + TP_printk("id=%d", __entry->id) +); + +DEFINE_EVENT(sparse_free_template, sparse_phys_free, + TP_PROTO(unsigned int id), + TP_ARGS(id) +); + +DEFINE_EVENT(sparse_free_template, sparse_virt_free, + TP_PROTO(unsigned int id), + TP_ARGS(id) +); + +TRACE_EVENT(sparse_bind, + TP_PROTO(unsigned int v_id, uint64_t v_off, + unsigned int p_id, uint64_t p_off, + uint64_t size, uint64_t flags), + TP_ARGS(v_id, v_off, p_id, p_off, size, flags), + TP_STRUCT__entry( + __field(unsigned int, v_id) + __field(uint64_t, v_off) + __field(unsigned int, p_id) + __field(uint64_t, p_off) + __field(uint64_t, size) + __field(uint64_t, flags) + ), + TP_fast_assign( + __entry->v_id = v_id; + __entry->v_off = v_off; + __entry->p_id = p_id; + __entry->p_off = p_off; + __entry->size = size; + __entry->flags = flags; + ), + TP_printk( + "v_id=%d v_off=0x%llX p_id=%d p_off=0x%llX size=0x%llX flags=0x%llX", + __entry->v_id, __entry->v_off, + __entry->p_id, __entry->p_off, + __entry->size, __entry->flags) +); + +TRACE_EVENT(sparse_unbind, + TP_PROTO(unsigned int v_id, uint64_t v_off, uint64_t size), + TP_ARGS(v_id, v_off, size), + TP_STRUCT__entry( + __field(unsigned int, v_id) + __field(uint64_t, v_off) + __field(uint64_t, size) + ), + TP_fast_assign( + __entry->v_id = v_id; + __entry->v_off = v_off; + __entry->size = size; + ), + TP_printk("v_id=%d v_off=0x%llX size=0x%llX", + __entry->v_id, __entry->v_off, __entry->size) +); + #endif /* _KGSL_TRACE_H */ diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 45ce036b1693..eac9067822a3 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -18,6 +18,7 @@ #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -27,6 +28,7 @@ #include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <linux/firmware.h> +#include <linux/debugfs.h> #include <linux/input/ft5x06_ts.h> #if defined(CONFIG_FB) @@ -39,22 +41,23 @@ #define FT_SUSPEND_LEVEL 1 #endif -#define CFG_MAX_TOUCH_POINTS 5 +#define FT_DRIVER_VERSION 0x02 -#define FT_STARTUP_DLY 150 -#define FT_RESET_DLY 20 +#define FT_META_REGS 3 +#define FT_ONE_TCH_LEN 6 +#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x) #define FT_PRESS 0x7F #define FT_MAX_ID 0x0F -#define FT_TOUCH_STEP 6 #define FT_TOUCH_X_H_POS 3 #define FT_TOUCH_X_L_POS 4 #define FT_TOUCH_Y_H_POS 5 #define FT_TOUCH_Y_L_POS 6 +#define FT_TD_STATUS 2 #define FT_TOUCH_EVENT_POS 3 #define FT_TOUCH_ID_POS 5 - -#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS) +#define FT_TOUCH_DOWN 0 +#define FT_TOUCH_CONTACT 2 /* register address*/ #define FT_REG_DEV_MODE 0x00 @@ -66,6 +69,9 @@ #define FT_REG_THGROUP 0x80 #define FT_REG_ECC 0xCC #define FT_REG_RESET_FW 0x07 +#define FT_REG_FW_MAJ_VER 0xB1 +#define FT_REG_FW_MIN_VER 0xB2 +#define FT_REG_FW_SUB_MIN_VER 0xB3 /* power register bits */ #define FT_PMODE_ACTIVE 0x00 @@ -74,12 +80,14 @@ #define FT_PMODE_HIBERNATE 0x03 #define FT_FACTORYMODE_VALUE 0x40 #define FT_WORKMODE_VALUE 0x00 -#define FT_RST_CMD_REG 0xFC +#define FT_RST_CMD_REG1 0xFC +#define FT_RST_CMD_REG2 0xBC #define FT_READ_ID_REG 0x90 #define FT_ERASE_APP_REG 0x61 #define FT_ERASE_PANEL_REG 0x63 #define FT_FW_START_REG 0xBF +#define FT_STATUS_NUM_TP_MASK 0x0F #define FT_VTG_MIN_UV 2600000 #define FT_VTG_MAX_UV 3300000 @@ -95,54 +103,19 @@ #define FT5316_ID 0x0A #define FT5306I_ID 0x55 +#define FT6X06_ID 0x06 #define FT_UPGRADE_AA 0xAA #define FT_UPGRADE_55 0x55 -/* upgrade config of FT5606 */ -#define FT5606_UPGRADE_AA_DELAY 50 -#define FT5606_UPGRADE_55_DELAY 10 -#define FT5606_UPGRADE_ID_1 0x79 -#define FT5606_UPGRADE_ID_2 0x06 -#define FT5606_UPGRADE_READID_DELAY 100 -#define FT5606_UPGRADE_EARSE_DELAY 2000 - -/* upgrade config of FT5316 */ -#define FT5316_UPGRADE_AA_DELAY 50 -#define FT5316_UPGRADE_55_DELAY 30 -#define FT5316_UPGRADE_ID_1 0x79 -#define FT5316_UPGRADE_ID_2 0x07 -#define FT5316_UPGRADE_READID_DELAY 1 -#define FT5316_UPGRADE_EARSE_DELAY 1500 - -/* upgrade config of FT5x06(x=2,3,4) */ -#define FT5X06_UPGRADE_AA_DELAY 50 -#define FT5X06_UPGRADE_55_DELAY 30 -#define FT5X06_UPGRADE_ID_1 0x79 -#define FT5X06_UPGRADE_ID_2 0x03 -#define FT5X06_UPGRADE_READID_DELAY 1 -#define FT5X06_UPGRADE_EARSE_DELAY 2000 - -/* upgrade config of FT6208 */ -#define FT6208_UPGRADE_AA_DELAY 60 -#define FT6208_UPGRADE_55_DELAY 10 -#define FT6208_UPGRADE_ID_1 0x79 -#define FT6208_UPGRADE_ID_2 0x05 -#define FT6208_UPGRADE_READID_DELAY 10 -#define FT6208_UPGRADE_EARSE_DELAY 2000 - -#define FT_UPGRADE_INFO(x, y) do { \ - x->delay_55 = y##_UPGRADE_55_DELAY; \ - x->delay_aa = y##_UPGRADE_AA_DELAY; \ - x->upgrade_id_1 = y##_UPGRADE_ID_1; \ - x->upgrade_id_2 = y##_UPGRADE_ID_2; \ - x->delay_readid = y##_UPGRADE_READID_DELAY; \ - x->delay_earse_flash = y##_UPGRADE_EARSE_DELAY; \ - } while (0) - #define FT_FW_MIN_SIZE 8 #define FT_FW_MAX_SIZE 32768 -#define FT_FW_FILE_VER(x) ((x)->data[(x)->size - 2]) + +/* Firmware file is not supporting minor and sub minor so use 0 */ +#define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) +#define FT_FW_FILE_MIN_VER(x) 0 +#define FT_FW_FILE_SUB_MIN_VER(x) 0 + #define FT_FW_CHECK(x) \ (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ && (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ @@ -159,7 +132,7 @@ #define FT_FW_LAST_PKT 0x6ffa #define FT_EARSE_DLY_MS 100 -#define FT_UPGRADE_LOOP 3 +#define FT_UPGRADE_LOOP 10 #define FT_CAL_START 0x04 #define FT_CAL_FIN 0x00 #define FT_CAL_STORE 0x05 @@ -167,35 +140,42 @@ #define FT_REG_CAL 0x00 #define FT_CAL_MASK 0x70 -struct ts_event { - u16 x[CFG_MAX_TOUCH_POINTS]; /* x coordinate */ - u16 y[CFG_MAX_TOUCH_POINTS]; /* y coordinate */ - /* touch event: 0 -- down; 1-- contact; 2 -- contact */ - u8 touch_event[CFG_MAX_TOUCH_POINTS]; - u8 finger_id[CFG_MAX_TOUCH_POINTS]; /* touch ID */ - u16 pressure; - u8 touch_point; -}; - -struct upgrade_info { - u16 delay_aa; /* delay of write FT_UPGRADE_AA */ - u16 delay_55; /* delay of write FT_UPGRADE_55 */ - u8 upgrade_id_1; /* upgrade id 1 */ - u8 upgrade_id_2; /* upgrade id 2 */ - u16 delay_readid; /* delay of read id */ - u16 delay_earse_flash; /* delay of earse flash */ -}; +#define FT_INFO_MAX_LEN 512 + +#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_vkey_support, \ + fw_name, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "controller\t= focaltech\n" \ + "model\t\t= 0x%x\n" \ + "name\t\t= %s\n" \ + "max_touches\t= %d\n" \ + "drv_ver\t\t= 0x%x\n" \ + "group_id\t= 0x%x\n" \ + "fw_vkey_support\t= %s\n" \ + "fw_name\t\t= %s\n" \ + "fw_ver\t\t= %d.%d.%d\n", id, name, \ + max_tch, FT_DRIVER_VERSION, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, \ + fw_sub_min) + +#define FT_DEBUG_DIR_NAME "ts_debug" struct ft5x06_ts_data { struct i2c_client *client; struct input_dev *input_dev; - struct ts_event event; const struct ft5x06_ts_platform_data *pdata; struct regulator *vdd; struct regulator *vcc_i2c; char fw_name[FT_FW_NAME_MAX_LEN]; bool loading_fw; u8 family_id; + struct dentry *dir; + u16 addr; + bool suspended; + char *ts_info; + u8 *tch_data; + u32 tch_data_len; + u8 fw_ver[3]; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -278,85 +258,91 @@ static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val) return ft5x06_i2c_read(client, &addr, 1, val, 1); } -static void ft5x06_report_value(struct ft5x06_ts_data *data) +static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data) { - struct ts_event *event = &data->event; - int i; - int fingerdown = 0; - - for (i = 0; i < event->touch_point; i++) { - if (event->touch_event[i] == 0 || event->touch_event[i] == 2) { - event->pressure = FT_PRESS; - fingerdown++; - } else { - event->pressure = 0; - } + struct i2c_client *client = data->client; + u8 reg_addr; + int err; - input_report_abs(data->input_dev, ABS_MT_POSITION_X, - event->x[i]); - input_report_abs(data->input_dev, ABS_MT_POSITION_Y, - event->y[i]); - input_report_abs(data->input_dev, ABS_MT_PRESSURE, - event->pressure); - input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, - event->finger_id[i]); - input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, - event->pressure); - input_mt_sync(data->input_dev); - } + reg_addr = FT_REG_FW_MAJ_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); + if (err < 0) + dev_err(&client->dev, "fw major version read failed"); - input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); - input_sync(data->input_dev); + reg_addr = FT_REG_FW_MIN_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[1], 1); + if (err < 0) + dev_err(&client->dev, "fw minor version read failed"); + + reg_addr = FT_REG_FW_SUB_MIN_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[2], 1); + if (err < 0) + dev_err(&client->dev, "fw sub minor version read failed"); + + dev_info(&client->dev, "Firmware version = %d.%d.%d\n", + data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); } -static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) +static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) { - struct ts_event *event = &data->event; - int ret, i; - u8 buf[POINT_READ_BUF] = { 0 }; - u8 pointid = FT_MAX_ID; - - ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF); - if (ret < 0) { - dev_err(&data->client->dev, "%s read touchdata failed.\n", - __func__); - return ret; - } - memset(event, 0, sizeof(struct ts_event)); - - event->touch_point = 0; - for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { - pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; - if (pointid >= FT_MAX_ID) - break; + struct ft5x06_ts_data *data = dev_id; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, pressure, status, num_touches; + u8 reg = 0x00, *buf; + bool update_input = false; + + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } - event->touch_point++; + ip_dev = data->input_dev; + buf = data->tch_data; - event->x[i] = - (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) << - 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i]; - event->y[i] = - (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) << - 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i]; - event->touch_event[i] = - buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6; - event->finger_id[i] = - (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; + rc = ft5x06_i2c_read(data->client, ®, 1, + buf, data->tch_data_len); + if (rc < 0) { + dev_err(&data->client->dev, "%s: read data fail\n", __func__); + return IRQ_HANDLED; } - ft5x06_report_value(data); + for (i = 0; i < data->pdata->num_max_touches; i++) { + id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; + if (id >= FT_MAX_ID) + break; - return 0; -} + update_input = true; -static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) -{ - struct ft5x06_ts_data *data = dev_id; - int rc; + x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]); + y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]); - rc = ft5x06_handle_touchdata(data); - if (rc) - pr_err("%s: handling touchdata failed\n", __func__); + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + input_mt_slot(ip_dev, id); + if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { + pressure = FT_PRESS; + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, x); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); + input_report_abs(ip_dev, ABS_MT_PRESSURE, pressure); + } else { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } + + if (update_input) { + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } return IRQ_HANDLED; } @@ -473,30 +459,99 @@ pwr_deinit: static int ft5x06_ts_suspend(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); - char txbuf[2]; + char txbuf[2], i; + int err; + + if (data->loading_fw) { + dev_info(dev, "Firmware loading in process...\n"); + return 0; + } + + if (data->suspended) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } disable_irq(data->client->irq); + /* release all touches */ + for (i = 0; i < data->pdata->num_max_touches; i++) { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); + } + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_sync(data->input_dev); + if (gpio_is_valid(data->pdata->reset_gpio)) { txbuf[0] = FT_REG_PMODE; txbuf[1] = FT_PMODE_HIBERNATE; ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); } + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } else { + err = ft5x06_power_on(data, false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } + + data->suspended = true; + return 0; + +pwr_off_fail: + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + enable_irq(data->client->irq); + return err; } static int ft5x06_ts_resume(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; + + if (!data->suspended) { + dev_dbg(dev, "Already in awake state\n"); + return 0; + } + + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); - msleep(FT_RESET_DLY); + msleep(data->pdata->hard_rst_dly); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } + + msleep(data->pdata->soft_rst_dly); + enable_irq(data->client->irq); + data->suspended = false; + return 0; } @@ -550,16 +605,17 @@ static const struct dev_pm_ops ft5x06_ts_pm_ops = { static int ft5x06_auto_cal(struct i2c_client *client) { + struct ft5x06_ts_data *data = i2c_get_clientdata(client); u8 temp = 0, i; /* set to factory mode */ - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* start calibration */ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); for (i = 0; i < FT_CAL_RETRY; i++) { ft5x0x_read_reg(client, FT_REG_CAL, &temp); /* return to normal mode, calibration finish */ @@ -567,34 +623,18 @@ static int ft5x06_auto_cal(struct i2c_client *client) break; } - /* calibration OK */ - msleep(2 * FT_STARTUP_DLY); + /*calibration OK */ + msleep(2 * data->pdata->soft_rst_dly); ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* store calibration data */ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); /* set to normal mode */ ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); - msleep(2 * FT_STARTUP_DLY); - - return 0; -} - -static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info) -{ - switch (family_id) { - case FT5306I_ID: - FT_UPGRADE_INFO(info, FT5X06); - break; - case FT5316_ID: - FT_UPGRADE_INFO(info, FT5316); - break; - default: - return -EINVAL; - } + msleep(2 * data->pdata->soft_rst_dly); return 0; } @@ -603,35 +643,35 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, const u8 *data, u32 data_len) { struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client); - struct upgrade_info info; + struct fw_upgrade_info info = ts_data->pdata->info; + u8 reset_reg; u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; int rc, i, j, temp; u32 pkt_num, pkt_len; u8 fw_ecc; - rc = ft5x06_get_upgrade_info(ts_data->family_id, &info); - if (rc < 0) { - dev_err(&client->dev, "Cannot get upgrade information!\n"); - return -EINVAL; - } + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + /* reset - write 0xaa and 0x55 to reset register */ + if (ts_data->family_id == FT6X06_ID) + reset_reg = FT_RST_CMD_REG2; + else + reset_reg = FT_RST_CMD_REG1; - for (i = 0; i < FT_UPGRADE_LOOP; i++) { - /* reset - write 0xaa and 0x55 to register 0xfc */ - ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_AA); + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_AA); msleep(info.delay_aa); - ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55); + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55); msleep(info.delay_55); /* Enter upgrade mode */ w_buf[0] = FT_UPGRADE_55; w_buf[1] = FT_UPGRADE_AA; do { - i++; + j++; rc = ft5x06_i2c_write(client, w_buf, 2); msleep(FT_RETRY_DLY); - } while (rc <= 0 && i < FT_MAX_TRIES); + } while (rc <= 0 && j < FT_MAX_TRIES); /* check READ_ID */ msleep(info.delay_readid); @@ -657,7 +697,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, /* erase app and panel paramenter area */ w_buf[0] = FT_ERASE_APP_REG; ft5x06_i2c_write(client, w_buf, 1); - msleep(info.delay_earse_flash); + msleep(info.delay_erase_flash); w_buf[0] = FT_ERASE_PANEL_REG; ft5x06_i2c_write(client, w_buf, 1); @@ -732,7 +772,9 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, /* reset */ w_buf[0] = FT_REG_RESET_FW; ft5x06_i2c_write(client, w_buf, 1); - msleep(FT_STARTUP_DLY); + msleep(ts_data->pdata->soft_rst_dly); + + dev_info(&client->dev, "Firmware upgrade successful\n"); return 0; } @@ -742,7 +784,8 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) struct ft5x06_ts_data *data = dev_get_drvdata(dev); const struct firmware *fw = NULL; int rc; - u8 val = 0; + u8 fw_file_maj, fw_file_min, fw_file_sub_min; + bool fw_upgrade = false; rc = request_firmware(&fw, data->fw_name, dev); if (rc < 0) { @@ -757,34 +800,52 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) goto rel_fw; } - /* check firmware version */ - rc = ft5x0x_read_reg(data->client, FT_REG_FW_VER, &val); - if (rc < 0) { - dev_err(dev, "Get firmware version failed\n"); - goto rel_fw; - } + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_min = FT_FW_FILE_MIN_VER(fw); + fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + + dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, + fw_file_min, fw_file_sub_min); + + if (force) { + fw_upgrade = true; + } else if (data->fw_ver[0] == fw_file_maj) { + if (data->fw_ver[1] < fw_file_min) + fw_upgrade = true; + else if (data->fw_ver[2] < fw_file_sub_min) + fw_upgrade = true; + else + dev_info(dev, "No need to upgrade\n"); + } else + dev_info(dev, "Firmware versions do not match\n"); - if (val == FT_FW_FILE_VER(fw) && !force) { - dev_err(dev, "No need to update (0x%x)\n", val); + if (!fw_upgrade) { + dev_info(dev, "Exiting fw upgrade...\n"); rc = -EFAULT; goto rel_fw; } - dev_info(dev, "upgrade to fw ver 0x%x from 0x%x\n", - FT_FW_FILE_VER(fw), val); - /* start firmware upgrade */ if (FT_FW_CHECK(fw)) { rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); if (rc < 0) - dev_err(dev, "update failed (%d)\n", rc); - else + dev_err(dev, "update failed (%d). try later...\n", rc); + else if (data->pdata->info.auto_cal) ft5x06_auto_cal(data->client); } else { dev_err(dev, "FW format error\n"); rc = -EIO; } + ft5x06_update_fw_ver(data); + + FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); rel_fw: release_firmware(fw); return rc; @@ -813,6 +874,11 @@ static ssize_t ft5x06_update_fw_store(struct device *dev, if (rc != 0) return rc; + if (data->suspended) { + dev_info(dev, "In suspend state, try again later...\n"); + return size; + } + mutex_lock(&data->input_dev->mutex); if (!data->loading_fw && val) { data->loading_fw = true; @@ -882,6 +948,138 @@ static ssize_t ft5x06_fw_name_store(struct device *dev, static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store); +static bool ft5x06_debug_addr_is_valid(int addr) +{ + if (addr < 0 || addr > 0xFF) { + pr_err("FT reg address is invalid: 0x%x\n", addr); + return false; + } + + return true; +} + +static int ft5x06_debug_data_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) + dev_info(&data->client->dev, + "Writing into FT registers not supported\n"); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft5x06_debug_data_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + int rc; + u8 reg; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) { + rc = ft5x0x_read_reg(data->client, data->addr, ®); + if (rc < 0) + dev_err(&data->client->dev, + "FT read register 0x%x failed (%d)\n", + data->addr, rc); + else + *val = reg; + } + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft5x06_debug_data_get, + ft5x06_debug_data_set, "0x%02llX\n"); + +static int ft5x06_debug_addr_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + if (ft5x06_debug_addr_is_valid(val)) { + mutex_lock(&data->input_dev->mutex); + data->addr = val; + mutex_unlock(&data->input_dev->mutex); + } + + return 0; +} + +static int ft5x06_debug_addr_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) + *val = data->addr; + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get, + ft5x06_debug_addr_set, "0x%02llX\n"); + +static int ft5x06_debug_suspend_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + ft5x06_ts_suspend(&data->client->dev); + else + ft5x06_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft5x06_debug_suspend_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + *val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get, + ft5x06_debug_suspend_set, "%lld\n"); + +static int ft5x06_debug_dump_info(struct seq_file *m, void *v) +{ + struct ft5x06_ts_data *data = m->private; + + seq_printf(m, "%s\n", data->ts_info); + + return 0; +} + +static int debugfs_dump_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, ft5x06_debug_dump_info, inode->i_private); +} + +static const struct file_operations debug_dump_info_fops = { + .owner = THIS_MODULE, + .open = debugfs_dump_info_open, + .read = seq_read, + .release = single_release, +}; + #ifdef CONFIG_OF static int ft5x06_get_dt_coords(struct device *dev, char *name, struct ft5x06_ts_platform_data *pdata) @@ -936,6 +1134,13 @@ static int ft5x06_parse_dt(struct device *dev, u32 temp_val, num_buttons; u32 button_map[MAX_BUTTONS]; + pdata->name = "focaltech"; + rc = of_property_read_string(np, "focaltech,name", &pdata->name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read name\n"); + return rc; + } + rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata); if (rc && (rc != -EINVAL)) return rc; @@ -960,6 +1165,89 @@ static int ft5x06_parse_dt(struct device *dev, if (pdata->irq_gpio < 0) return pdata->irq_gpio; + pdata->fw_name = "ft_fw.bin"; + rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); + if (!rc) + pdata->group_id = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", + &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay aa\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_aa = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay 55\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_55 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id1\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_1 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id2\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_2 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay read id\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_readid = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay erase flash\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_erase_flash = temp_val; + + pdata->info.auto_cal = of_property_read_bool(np, + "focaltech,fw-auto-cal"); + + pdata->fw_vkey_support = of_property_read_bool(np, + "focaltech,fw-vkey-support"); + rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); if (!rc) pdata->family_id = temp_val; @@ -997,9 +1285,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, struct ft5x06_ts_platform_data *pdata; struct ft5x06_ts_data *data; struct input_dev *input_dev; + struct dentry *temp; u8 reg_value; u8 reg_addr; - int err; + int err, len; if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, @@ -1008,8 +1297,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, return -ENOMEM; err = ft5x06_parse_dt(&client->dev, pdata); - if (err) + if (err) { + dev_err(&client->dev, "DT parsing failed\n"); return err; + } } else pdata = client->dev.platform_data; @@ -1023,15 +1314,31 @@ static int ft5x06_ts_probe(struct i2c_client *client, return -ENODEV; } - data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdata->fw_name) { + len = strlen(pdata->fw_name); + if (len > FT_FW_NAME_MAX_LEN - 1) { + dev_err(&client->dev, "Invalid firmware name\n"); + return -EINVAL; + } + + strlcpy(data->fw_name, pdata->fw_name, len + 1); + } + + data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); + data->tch_data = devm_kzalloc(&client->dev, + data->tch_data_len, GFP_KERNEL); if (!data) return -ENOMEM; input_dev = input_allocate_device(); if (!input_dev) { - err = -ENOMEM; dev_err(&client->dev, "failed to allocate input device\n"); - goto free_mem; + return -ENOMEM; } data->input_dev = input_dev; @@ -1050,13 +1357,11 @@ static int ft5x06_ts_probe(struct i2c_client *client, __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + input_mt_init_slots(input_dev, pdata->num_max_touches); input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, - CFG_MAX_TOUCH_POINTS, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0); input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); err = input_register_device(input_dev); @@ -1120,19 +1425,19 @@ static int ft5x06_ts_probe(struct i2c_client *client, "set_direction for reset gpio failed\n"); goto free_reset_gpio; } - msleep(FT_RESET_DLY); + msleep(data->pdata->hard_rst_dly); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } /* make sure CTP already finish startup process */ - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* check the controller id */ reg_addr = FT_REG_ID; err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); if (err < 0) { dev_err(&client->dev, "version read failed"); - return err; + goto free_reset_gpio; } dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); @@ -1144,28 +1449,6 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->family_id = reg_value; - /* get some register information */ - reg_addr = FT_REG_FW_VER; - err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err < 0) - dev_err(&client->dev, "version read failed"); - - dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value); - - reg_addr = FT_REG_POINT_RATE; - ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err < 0) - dev_err(&client->dev, "report rate read failed"); - - dev_dbg(&client->dev, "report rate = %dHz\n", reg_value * 10); - - reg_addr = FT_REG_THGROUP; - err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err < 0) - dev_err(&client->dev, "threshold read failed"); - - dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); - err = request_threaded_irq(client->irq, NULL, ft5x06_ts_interrupt, pdata->irqflags, client->dev.driver->name, data); @@ -1192,6 +1475,72 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_update_fw_sys; } + data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); + if (data->dir == NULL || IS_ERR(data->dir)) { + pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir)); + err = PTR_ERR(data->dir); + goto free_force_update_fw_sys; + } + + temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data, + &debug_addr_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data, + &debug_data_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir, + data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir, + data, &debug_dump_info_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + data->ts_info = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!data->ts_info) + goto free_debug_dir; + + /*get some register information */ + reg_addr = FT_REG_POINT_RATE; + ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "report rate read failed"); + + dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10); + + reg_addr = FT_REG_THGROUP; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "threshold read failed"); + + dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); + + ft5x06_update_fw_ver(data); + + FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); #if defined(CONFIG_FB) data->fb_notif.notifier_call = fb_notifier_callback; @@ -1211,6 +1560,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, return 0; +free_debug_dir: + debugfs_remove_recursive(data->dir); +free_force_update_fw_sys: + device_remove_file(&client->dev, &dev_attr_force_update_fw); free_update_fw_sys: device_remove_file(&client->dev, &dev_attr_update_fw); free_fw_name_sys: @@ -1238,8 +1591,6 @@ unreg_inputdev: input_dev = NULL; free_inputdev: input_free_device(input_dev); -free_mem: - kfree(data); return err; } @@ -1247,6 +1598,7 @@ static int ft5x06_ts_remove(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); + debugfs_remove_recursive(data->dir); device_remove_file(&client->dev, &dev_attr_force_update_fw); device_remove_file(&client->dev, &dev_attr_update_fw); device_remove_file(&client->dev, &dev_attr_fw_name); @@ -1276,7 +1628,6 @@ static int ft5x06_ts_remove(struct i2c_client *client) ft5x06_power_init(data, false); input_unregister_device(data->input_dev); - kfree(data); return 0; } diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c index 3dfe4e1d334e..bef080671ab0 100644 --- a/drivers/input/touchscreen/gt9xx/goodix_tool.c +++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c @@ -1,615 +1,551 @@ -/* drivers/input/touchscreen/goodix_tool.c
- *
- * 2010 - 2012 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * 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.
- *
- * Version:1.6
- * V1.0:2012/05/01,create file.
- * V1.2:2012/06/08,modify some warning.
- * V1.4:2012/08/28,modified to support GT9XX
- * V1.6:new proc name
- */
-
-#include "gt9xx.h"
-
-#define DATA_LENGTH_UINT 512
-#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8*))
-static char procname[20] = {0};
-
-#define UPDATE_FUNCTIONS
-
-#ifdef UPDATE_FUNCTIONS
-extern s32 gup_enter_update_mode(struct i2c_client *client);
-extern void gup_leave_update_mode(void);
-extern s32 gup_update_proc(void *dir);
-#endif
-
-extern void gtp_irq_disable(struct goodix_ts_data *);
-extern void gtp_irq_enable(struct goodix_ts_data *);
-
-#pragma pack(1)
-typedef struct{
- u8 wr; //write read flag£¬0:R 1:W 2:PID 3:
- u8 flag; //0:no need flag/int 1: need flag 2:need int
- u8 flag_addr[2]; //flag address
- u8 flag_val; //flag val
- u8 flag_relation; //flag_val:flag 0:not equal 1:equal 2:> 3:<
- u16 circle; //polling cycle
- u8 times; //plling times
- u8 retry; //I2C retry times
- u16 delay; //delay befor read or after write
- u16 data_len; //data length
- u8 addr_len; //address length
- u8 addr[2]; //address
- u8 res[3]; //reserved
- u8* data; //data pointer
-}st_cmd_head;
-#pragma pack()
-st_cmd_head cmd_head;
-
-static struct i2c_client *gt_client = NULL;
-
-static struct proc_dir_entry *goodix_proc_entry;
-
-static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data);
-static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data );
-static s32 (*tool_i2c_read)(u8 *, u16);
-static s32 (*tool_i2c_write)(u8 *, u16);
-
-#if GTP_ESD_PROTECT
-extern void gtp_esd_switch(struct i2c_client *, s32);
-#endif
-s32 DATA_LENGTH = 0;
-s8 IC_TYPE[16] = {0};
-
-static void tool_set_proc_name(char * procname)
-{
- char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
- "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- char date[20] = {0};
- char month[4] = {0};
- int i = 0, n_month = 1, n_day = 0, n_year = 0;
-
- sprintf(date, "%s", __DATE__);
-
- //GTP_DEBUG("compile date: %s", date);
-
- sscanf(date, "%s %d %d", month, &n_day, &n_year);
-
- for (i = 0; i < 12; ++i)
- {
- if (!memcmp(months[i], month, 3))
- {
- n_month = i+1;
- break;
- }
- }
-
- sprintf(procname, "gmnode%04d%02d%02d", n_year, n_month, n_day);
-
- //GTP_DEBUG("procname = %s", procname);
-}
-
-
-static s32 tool_i2c_read_no_extra(u8* buf, u16 len)
-{
- s32 ret = -1;
- s32 i = 0;
- struct i2c_msg msgs[2];
-
- msgs[0].flags = !I2C_M_RD;
- msgs[0].addr = gt_client->addr;
- msgs[0].len = cmd_head.addr_len;
- msgs[0].buf = &buf[0];
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = gt_client->addr;
- msgs[1].len = len;
- msgs[1].buf = &buf[GTP_ADDR_LENGTH];
-
- for (i = 0; i < cmd_head.retry; i++)
- {
- ret=i2c_transfer(gt_client->adapter, msgs, 2);
- if (ret > 0)
- {
- break;
- }
- }
- return ret;
-}
-
-static s32 tool_i2c_write_no_extra(u8* buf, u16 len)
-{
- s32 ret = -1;
- s32 i = 0;
- struct i2c_msg msg;
-
- msg.flags = !I2C_M_RD;
- msg.addr = gt_client->addr;
- msg.len = len;
- msg.buf = buf;
-
- for (i = 0; i < cmd_head.retry; i++)
- {
- ret=i2c_transfer(gt_client->adapter, &msg, 1);
- if (ret > 0)
- {
- break;
- }
- }
- return ret;
-}
-
-static s32 tool_i2c_read_with_extra(u8* buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_read_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static s32 tool_i2c_write_with_extra(u8* buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_write_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static void register_i2c_func(void)
-{
-// if (!strncmp(IC_TYPE, "GT818", 5) || !strncmp(IC_TYPE, "GT816", 5)
-// || !strncmp(IC_TYPE, "GT811", 5) || !strncmp(IC_TYPE, "GT818F", 6)
-// || !strncmp(IC_TYPE, "GT827", 5) || !strncmp(IC_TYPE,"GT828", 5)
-// || !strncmp(IC_TYPE, "GT813", 5))
- if (strncmp(IC_TYPE, "GT8110", 6) && strncmp(IC_TYPE, "GT8105", 6)
- && strncmp(IC_TYPE, "GT801", 5) && strncmp(IC_TYPE, "GT800", 5)
- && strncmp(IC_TYPE, "GT801PLUS", 9) && strncmp(IC_TYPE, "GT811", 5)
- && strncmp(IC_TYPE, "GTxxx", 5))
- {
- tool_i2c_read = tool_i2c_read_with_extra;
- tool_i2c_write = tool_i2c_write_with_extra;
- GTP_DEBUG("I2C function: with pre and end cmd!");
- }
- else
- {
- tool_i2c_read = tool_i2c_read_no_extra;
- tool_i2c_write = tool_i2c_write_no_extra;
- GTP_INFO("I2C function: without pre and end cmd!");
- }
-}
-
-static void unregister_i2c_func(void)
-{
- tool_i2c_read = NULL;
- tool_i2c_write = NULL;
- GTP_INFO("I2C function: unregister i2c transfer function!");
-}
-
-
-s32 init_wr_node(struct i2c_client *client)
-{
- s32 i;
-
- gt_client = client;
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_head.data = NULL;
-
- i = 5;
- while ((!cmd_head.data) && i)
- {
- cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
- if (NULL != cmd_head.data)
- {
- break;
- }
- i--;
- }
- if (i)
- {
- DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
- GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
- }
- else
- {
- GTP_ERROR("Apply for memory failed.");
- return FAIL;
- }
-
- cmd_head.addr_len = 2;
- cmd_head.retry = 5;
-
- register_i2c_func();
-
- tool_set_proc_name(procname);
- goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
- if (goodix_proc_entry == NULL)
- {
- GTP_ERROR("Couldn't create proc entry!");
- return FAIL;
- }
- else
- {
- GTP_INFO("Create proc entry success!");
- goodix_proc_entry->write_proc = goodix_tool_write;
- goodix_proc_entry->read_proc = goodix_tool_read;
- }
-
- return SUCCESS;
-}
-
-void uninit_wr_node(void)
-{
- kfree(cmd_head.data);
- cmd_head.data = NULL;
- unregister_i2c_func();
- remove_proc_entry(procname, NULL);
-}
-
-static u8 relation(u8 src, u8 dst, u8 rlt)
-{
- u8 ret = 0;
-
- switch (rlt)
- {
- case 0:
- ret = (src != dst) ? true : false;
- break;
-
- case 1:
- ret = (src == dst) ? true : false;
- GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", src, dst, (s32)ret);
- break;
-
- case 2:
- ret = (src > dst) ? true : false;
- break;
-
- case 3:
- ret = (src < dst) ? true : false;
- break;
-
- case 4:
- ret = (src & dst) ? true : false;
- break;
-
- case 5:
- ret = (!(src | dst)) ? true : false;
- break;
-
- default:
- ret = false;
- break;
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Comfirm function.
-Input:
- None.
-Output:
- Return write length.
-********************************************************/
-static u8 comfirm(void)
-{
- s32 i = 0;
- u8 buf[32];
-
-// memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], &cmd_head.flag_addr, cmd_head.addr_len);
-// memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);//Modified by Scott, 2012-02-17
- memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
-
- for (i = 0; i < cmd_head.times; i++)
- {
- if (tool_i2c_read(buf, 1) <= 0)
- {
- GTP_ERROR("Read flag data failed!");
- return FAIL;
- }
- if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, cmd_head.flag_relation))
- {
- GTP_DEBUG("value at flag addr:0x%02x.", buf[GTP_ADDR_LENGTH]);
- GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
- break;
- }
-
- msleep(cmd_head.circle);
- }
-
- if (i >= cmd_head.times)
- {
- GTP_ERROR("Didn't get the flag to continue!");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-/*******************************************************
-Function:
- Goodix tool write function.
-Input:
- standard proc write function param.
-Output:
- Return write length.
-********************************************************/
-static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
-{
- s32 ret = 0;
- GTP_DEBUG_FUNC();
- GTP_DEBUG_ARRAY((u8*)buff, len);
-
- ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
- if(ret)
- {
- GTP_ERROR("copy_from_user failed.");
- }
-
- GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
- GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
- GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
- GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
- GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
- GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
- GTP_DEBUG("times :%d.", (s32)cmd_head.times);
- GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
- GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
- GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
- GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
- GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
- GTP_DEBUG("len:%d.", (s32)len);
- GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
-
- if (1 == cmd_head.wr)
- {
- // copy_from_user(&cmd_head.data[cmd_head.addr_len], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- if(ret)
- {
- GTP_ERROR("copy_from_user failed.");
- }
- memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.addr, cmd_head.addr_len);
-
- GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len + cmd_head.addr_len);
- GTP_DEBUG_ARRAY((u8*)&buff[CMD_HEAD_LENGTH], cmd_head.data_len);
-
- if (1 == cmd_head.flag)
- {
- if (FAIL == comfirm())
- {
- GTP_ERROR("[WRITE]Comfirm fail!");
- return FAIL;
- }
- }
- else if (2 == cmd_head.flag)
- {
- //Need interrupt!
- }
- if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.data_len + cmd_head.addr_len) <= 0)
- {
- GTP_ERROR("[WRITE]Write data failed!");
- return FAIL;
- }
-
- GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],cmd_head.data_len + cmd_head.addr_len);
- if (cmd_head.delay)
- {
- msleep(cmd_head.delay);
- }
-
- return cmd_head.data_len + CMD_HEAD_LENGTH;
- }
- else if (3 == cmd_head.wr) //Write ic type
- {
- ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- if(ret)
- {
- GTP_ERROR("copy_from_user failed.");
- }
- memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
-
- register_i2c_func();
-
- return cmd_head.data_len + CMD_HEAD_LENGTH;
- }
- else if (5 == cmd_head.wr)
- {
- //memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
-
- return cmd_head.data_len + CMD_HEAD_LENGTH;
- }
- else if (7 == cmd_head.wr)//disable irq!
- {
- gtp_irq_disable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_OFF);
- #endif
- return CMD_HEAD_LENGTH;
- }
- else if (9 == cmd_head.wr) //enable irq!
- {
- gtp_irq_enable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_ON);
- #endif
- return CMD_HEAD_LENGTH;
- }
- else if(17 == cmd_head.wr)
- {
- struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
- ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- if(ret)
- {
- GTP_DEBUG("copy_from_user failed.");
- }
- if(cmd_head.data[GTP_ADDR_LENGTH])
- {
- GTP_DEBUG("gtp enter rawdiff.");
- ts->gtp_rawdiff_mode = true;
- }
- else
- {
- ts->gtp_rawdiff_mode = false;
- GTP_DEBUG("gtp leave rawdiff.");
- }
- return CMD_HEAD_LENGTH;
- }
-#ifdef UPDATE_FUNCTIONS
- else if (11 == cmd_head.wr)//Enter update mode!
- {
- if (FAIL == gup_enter_update_mode(gt_client))
- {
- return FAIL;
- }
- }
- else if (13 == cmd_head.wr)//Leave update mode!
- {
- gup_leave_update_mode();
- }
- else if (15 == cmd_head.wr) //Update firmware!
- {
- show_len = 0;
- total_len = 0;
- memset(cmd_head.data, 0, cmd_head.data_len + 1);
- memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
-
- if (FAIL == gup_update_proc((void*)cmd_head.data))
- {
- return FAIL;
- }
- }
-#endif
-
- return CMD_HEAD_LENGTH;
-}
-
-/*******************************************************
-Function:
- Goodix tool read function.
-Input:
- standard proc read function param.
-Output:
- Return read length.
-********************************************************/
-static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
-{
- GTP_DEBUG_FUNC();
-
- if (cmd_head.wr % 2)
- {
- return FAIL;
- }
- else if (!cmd_head.wr)
- {
- u16 len = 0;
- s16 data_len = 0;
- u16 loc = 0;
-
- if (1 == cmd_head.flag)
- {
- if (FAIL == comfirm())
- {
- GTP_ERROR("[READ]Comfirm fail!");
- return FAIL;
- }
- }
- else if (2 == cmd_head.flag)
- {
- //Need interrupt!
- }
-
- memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
-
- GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], cmd_head.data[1]);
- GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
-
- if (cmd_head.delay)
- {
- msleep(cmd_head.delay);
- }
-
- data_len = cmd_head.data_len;
- while(data_len > 0)
- {
- if (data_len > DATA_LENGTH)
- {
- len = DATA_LENGTH;
- }
- else
- {
- len = data_len;
- }
- data_len -= DATA_LENGTH;
-
- if (tool_i2c_read(cmd_head.data, len) <= 0)
- {
- GTP_ERROR("[READ]Read data failed!");
- return FAIL;
- }
- memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len);
- loc += len;
-
- GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
- GTP_DEBUG_ARRAY(page, len);
- }
- }
- else if (2 == cmd_head.wr)
- {
- // memcpy(page, "gt8", cmd_head.data_len);
- // memcpy(page, "GT818", 5);
- // page[5] = 0;
-
- GTP_DEBUG("Return ic type:%s len:%d.", page, (s32)cmd_head.data_len);
- return cmd_head.data_len;
- //return sizeof(IC_TYPE_NAME);
- }
- else if (4 == cmd_head.wr)
- {
- page[0] = show_len >> 8;
- page[1] = show_len & 0xff;
- page[2] = total_len >> 8;
- page[3] = total_len & 0xff;
-
- return cmd_head.data_len;
- }
- else if (6 == cmd_head.wr)
- {
- //Read error code!
- }
- else if (8 == cmd_head.wr) //Read driver version
- {
- // memcpy(page, GTP_DRIVER_VERSION, strlen(GTP_DRIVER_VERSION));
- s32 tmp_len;
- tmp_len = strlen(GTP_DRIVER_VERSION);
- memcpy(page, GTP_DRIVER_VERSION, tmp_len);
- page[tmp_len] = 0;
- }
-
- return cmd_head.data_len;
-}
+/* drivers/input/touchscreen/goodix_tool.c + * + * 2010 - 2012 Goodix Technology. + * Copyright (c) 2013-2016, 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + * Version:1.6 + * V1.0:2012/05/01,create file. + * V1.2:2012/06/08,modify some warning. + * V1.4:2012/08/28,modified to support GT9XX + * V1.6:new proc name + */ + +#include "gt9xx.h" + +#define DATA_LENGTH_UINT 512 +#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8 *)) +static char procname[20] = {0}; + +#define UPDATE_FUNCTIONS + +#pragma pack(1) +struct { + u8 wr; /* write read flag£¬0:R 1:W 2:PID 3: */ + u8 flag; /* 0:no need flag/int 1: need flag 2:need int */ + u8 flag_addr[2];/* flag address */ + u8 flag_val; /* flag val */ + u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */ + u16 circle; /* polling cycle */ + u8 times; /* plling times */ + u8 retry; /* I2C retry times */ + u16 delay; /* delay before read or after write */ + u16 data_len; /* data length */ + u8 addr_len; /* address length */ + u8 addr[2]; /* address */ + u8 res[3]; /* reserved */ + u8 *data; /* data pointer */ +} st_cmd_head; +#pragma pack() +st_cmd_head cmd_head; + +static struct i2c_client *gt_client; + +static struct proc_dir_entry *goodix_proc_entry; + +static s32 goodix_tool_write(struct file *filp, const char __user *buff, + unsigned long len, void *data); +static s32 goodix_tool_read(char *page, char **start, off_t off, int count, + int *eof, void *data); +static s32 (*tool_i2c_read)(u8 *, u16); +static s32 (*tool_i2c_write)(u8 *, u16); + +s32 DATA_LENGTH; +s8 IC_TYPE[16] = {0}; + +static void tool_set_proc_name(char *procname) +{ + char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char date[20] = {0}; + char month[4] = {0}; + int i = 0, n_month = 1, n_day = 0, n_year = 0, ret; + + ret = sscanf(date, "%s %d %d", month, &n_day, &n_year); + if (!ret) + return; + for (i = 0; i < 12; ++i) { + if (!memcmp(months[i], month, 3)) { + n_month = i+1; + break; + } + } + + snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day); + /* GTP_DEBUG("procname = %s", procname); */ +} + +static s32 tool_i2c_read_no_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + s32 i = 0; + struct i2c_msg msgs[2]; + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = gt_client->addr; + msgs[0].len = cmd_head.addr_len; + msgs[0].buf = &buf[0]; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = gt_client->addr; + msgs[1].len = len; + msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + + for (i = 0; i < cmd_head.retry; i++) { + ret = i2c_transfer(gt_client->adapter, msgs, 2); + if (ret > 0) + break; + } + + return ret; +} + +static s32 tool_i2c_write_no_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + s32 i = 0; + struct i2c_msg msg; + + msg.flags = !I2C_M_RD; + msg.addr = gt_client->addr; + msg.len = len; + msg.buf = buf; + + for (i = 0; i < cmd_head.retry; i++) { + ret = i2c_transfer(gt_client->adapter, &msg, 1); + if (ret > 0) + break; + } + + return ret; +} + +static s32 tool_i2c_read_with_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + u8 pre[2] = {0x0f, 0xff}; + u8 end[2] = {0x80, 0x00}; + + tool_i2c_write_no_extra(pre, 2); + ret = tool_i2c_read_no_extra(buf, len); + tool_i2c_write_no_extra(end, 2); + + return ret; +} + +static s32 tool_i2c_write_with_extra(u8 *buf, u16 len) +{ + s32 ret = -1; + u8 pre[2] = {0x0f, 0xff}; + u8 end[2] = {0x80, 0x00}; + + tool_i2c_write_no_extra(pre, 2); + ret = tool_i2c_write_no_extra(buf, len); + tool_i2c_write_no_extra(end, 2); + + return ret; +} + +static void register_i2c_func(void) +{ + if (strcmp(IC_TYPE, "GT8110") && strcmp(IC_TYPE, "GT8105") + && strcmp(IC_TYPE, "GT801") && strcmp(IC_TYPE, "GT800") + && strcmp(IC_TYPE, "GT801PLUS") && strcmp(IC_TYPE, "GT811") + && strcmp(IC_TYPE, "GTxxx")) { + tool_i2c_read = tool_i2c_read_with_extra; + tool_i2c_write = tool_i2c_write_with_extra; + GTP_DEBUG("I2C function: with pre and end cmd!"); + } else { + tool_i2c_read = tool_i2c_read_no_extra; + tool_i2c_write = tool_i2c_write_no_extra; + GTP_INFO("I2C function: without pre and end cmd!"); + } +} + +static void unregister_i2c_func(void) +{ + tool_i2c_read = NULL; + tool_i2c_write = NULL; + GTP_INFO("I2C function: unregister i2c transfer function!"); +} + +s32 init_wr_node(struct i2c_client *client) +{ + s32 i; + + gt_client = client; + memset(&cmd_head, 0, sizeof(cmd_head)); + cmd_head.data = NULL; + + i = 5; + while ((!cmd_head.data) && i) { + cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL); + if (cmd_head.data != NULL) + break; + i--; + } + if (i) { + DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH; + GTP_INFO("Applied memory size:%d.", DATA_LENGTH); + } else { + GTP_ERROR("Apply for memory failed."); + return FAIL; + } + + cmd_head.addr_len = 2; + cmd_head.retry = 5; + + register_i2c_func(); + + tool_set_proc_name(procname); + goodix_proc_entry = create_proc_entry(procname, 0666, NULL); + if (goodix_proc_entry == NULL) { + GTP_ERROR("Couldn't create proc entry!"); + return FAIL; + } + GTP_INFO("Create proc entry success!"); + goodix_proc_entry->write_proc = goodix_tool_write; + dix_proc_entry->read_proc = goodix_tool_read; + + return SUCCESS; +} + +void uninit_wr_node(void) +{ + kfree(cmd_head.data); + cmd_head.data = NULL; + unregister_i2c_func(); + remove_proc_entry(procname, NULL); +} + +static u8 relation(u8 src, u8 dst, u8 rlt) +{ + u8 ret = 0; + + switch (rlt) { + + case 0: + ret = (src != dst) ? true : false; + break; + + case 1: + ret = (src == dst) ? true : false; + GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", + src, dst, (s32)ret); + break; + + case 2: + ret = (src > dst) ? true : false; + break; + + case 3: + ret = (src < dst) ? true : false; + break; + + case 4: + ret = (src & dst) ? true : false; + break; + + case 5: + ret = (!(src | dst)) ? true : false; + break; + + default: + ret = false; + break; + } + + return ret; +} + +/******************************************************* + * Function: + * Comfirm function. + * Input: + * None. + * Output: + * Return write length. + ******************************************************* + */ +static u8 comfirm(void) +{ + s32 i = 0; + u8 buf[32]; + +/* memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], + * &cmd_head.flag_addr, cmd_head.addr_len); + * memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len); + * //Modified by Scott, 2012-02-17 + */ + memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len); + + for (i = 0; i < cmd_head.times; i++) { + if (tool_i2c_read(buf, 1) <= 0) { + GTP_ERROR("Read flag data failed!"); + return FAIL; + } + if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, + cmd_head.flag_relation)) { + GTP_DEBUG("value at flag addr:0x%02x.", + buf[GTP_ADDR_LENGTH]); + GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val); + break; + } + + msleep(cmd_head.circle); + } + + if (i >= cmd_head.times) { + GTP_ERROR("Didn't get the flag to continue!"); + return FAIL; + } + + return SUCCESS; +} + +/******************************************************** + * Function: + * Goodix tool write function. + * Input: + * standard proc write function param. + * Output: + * Return write length. + ******************************************************* + */ +static s32 goodix_tool_write(struct file *filp, const char __user *buff, + unsigned long len, void *data) +{ + s32 ret = 0; + + GTP_DEBUG_FUNC(); + GTP_DEBUG_ARRAY((u8 *)buff, len); + + ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH); + if (ret) + GTP_ERROR("copy_from_user failed."); + + GTP_DEBUG("wr :0x%02x.", cmd_head.wr); + GTP_DEBUG("flag:0x%02x.", cmd_head.flag); + GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], + cmd_head.flag_addr[1]); + GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val); + GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation); + GTP_DEBUG("circle :%d.", (s32)cmd_head.circle); + GTP_DEBUG("times :%d.", (s32)cmd_head.times); + GTP_DEBUG("retry :%d.", (s32)cmd_head.retry); + GTP_DEBUG("delay :%d.", (s32)cmd_head.delay); + GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len); + GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len); + GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]); + GTP_DEBUG("len:%d.", (s32)len); + GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]); + + if (cmd_head.wr == 1) { + /* copy_from_user(&cmd_head.data[cmd_head.addr_len], + * &buff[CMD_HEAD_LENGTH], cmd_head.data_len); + */ + ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], + &buff[CMD_HEAD_LENGTH], cmd_head.data_len); + if (ret) + GTP_ERROR("copy_from_user failed."); + + memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], + cmd_head.addr, cmd_head.addr_len); + + GTP_DEBUG_ARRAY(cmd_head.data, + cmd_head.data_len + cmd_head.addr_len); + GTP_DEBUG_ARRAY((u8 *)&buff[CMD_HEAD_LENGTH], + cmd_head.data_len); + + if (cmd_head.flag == 1) { + if (comfirm() == FAIL) { + GTP_ERROR("[WRITE]Comfirm fail!"); + return FAIL; + } + } else if (cmd_head.flag == 2) { + /* Need interrupt! */ + } + if (tool_i2c_write( + &cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], + cmd_head.data_len + cmd_head.addr_len) <= 0) { + GTP_ERROR("[WRITE]Write data failed!"); + return FAIL; + } + + GTP_DEBUG_ARRAY( + &cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], + cmd_head.data_len + cmd_head.addr_len); + if (cmd_head.delay) + msleep(cmd_head.delay); + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (cmd_head.wr == 3) { /* Write ic type */ + + ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], + cmd_head.data_len); + if (ret) + GTP_ERROR("copy_from_user failed."); + + memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); + + register_i2c_func(); + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (cmd_head.wr == 3) { + + /* memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); */ + + return cmd_head.data_len + CMD_HEAD_LENGTH; + } else if (cmd_head.wr == 7) { /* disable irq! */ + gtp_irq_disable(i2c_get_clientdata(gt_client)); + + #if GTP_ESD_PROTECT + gtp_esd_switch(gt_client, SWITCH_OFF); + #endif + return CMD_HEAD_LENGTH; + } else if (cmd_head.wr == 9) { /* enable irq! */ + gtp_irq_enable(i2c_get_clientdata(gt_client)); + + #if GTP_ESD_PROTECT + gtp_esd_switch(gt_client, SWITCH_ON); + #endif + return CMD_HEAD_LENGTH; + } else if (cmd_head.wr == 17) { + struct goodix_ts_data *ts = i2c_get_clientdata(gt_client); + + ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], + &buff[CMD_HEAD_LENGTH], cmd_head.data_len); + if (ret) + GTP_DEBUG("copy_from_user failed."); + if (cmd_head.data[GTP_ADDR_LENGTH]) { + GTP_DEBUG("gtp enter rawdiff."); + ts->gtp_rawdiff_mode = true; + } else { + ts->gtp_rawdiff_mode = false; + GTP_DEBUG("gtp leave rawdiff."); + } + return CMD_HEAD_LENGTH; + } +#ifdef UPDATE_FUNCTIONS + else if (cmd_head.wr == 11) { /* Enter update mode! */ + if (gup_enter_update_mode(gt_client) == FAIL) + return FAIL; + } else if (cmd_head.wr == 13) { /* Leave update mode! */ + gup_leave_update_mode(); + } else if (cmd_head.wr == 15) { /* Update firmware! */ + show_len = 0; + total_len = 0; + memset(cmd_head.data, 0, cmd_head.data_len + 1); + memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], + cmd_head.data_len); + + if (gup_update_proc((void *)cmd_head.data) == FAIL) + return FAIL; + } +#endif + + return CMD_HEAD_LENGTH; +} + +/******************************************************* + * Function: + * Goodix tool read function. + * Input: + * standard proc read function param. + * Output: + * Return read length. + ******************************************************* +*/ +static s32 goodix_tool_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + GTP_DEBUG_FUNC(); + + if (cmd_head.wr % 2) { + return FAIL; + } else if (!cmd_head.wr) { + u16 len = 0; + s16 data_len = 0; + u16 loc = 0; + + if (cmd_head.flag == 1) { + if (comfirm() == FAIL) { + GTP_ERROR("[READ]Comfirm fail!"); + return FAIL; + } + } else if (cmd_head.flag == 2) { + /* Need interrupt! */ + } + + memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len); + + GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], + cmd_head.data[1]); + GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], + cmd_head.addr[1]); + + if (cmd_head.delay) + msleep(cmd_head.delay); + + data_len = cmd_head.data_len; + while (data_len > 0) { + if (data_len > DATA_LENGTH) + len = DATA_LENGTH; + else + len = data_len; + + data_len -= DATA_LENGTH; + + if (tool_i2c_read(cmd_head.data, len) <= 0) { + GTP_ERROR("[READ]Read data failed!"); + return FAIL; + } + memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], + len); + loc += len; + + GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len); + GTP_DEBUG_ARRAY(page, len); + } + } else if (cmd_head.wr == 2) { + /* memcpy(page, "gt8", cmd_head.data_len); + * memcpy(page, "GT818", 5); + * page[5] = 0; + */ + + GTP_DEBUG("Return ic type:%s len:%d.", page, + (s32)cmd_head.data_len); + return cmd_head.data_len; + /* return sizeof(IC_TYPE_NAME); */ + } else if (cmd_head.wr == 4) { + page[0] = show_len >> 8; + page[1] = show_len & 0xff; + page[2] = total_len >> 8; + page[3] = total_len & 0xff; + + return cmd_head.data_len; + } else if (cmd_head.wr == 6) { + /* Read error code! */ + } else if (cmd_head.wr == 8) { /*Read driver version */ + /* memcpy(page, GTP_DRIVER_VERSION, + * strlen(GTP_DRIVER_VERSION)); + */ + s32 tmp_len; + + tmp_len = strlen(GTP_DRIVER_VERSION); + memcpy(page, GTP_DRIVER_VERSION, tmp_len); + page[tmp_len] = 0; + } + + return cmd_head.data_len; +} diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h index 3998bf0023f8..81e3affe62e9 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h +++ b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h @@ -1,6 +1,6 @@ -// make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled
-// define your own firmware array here
-const unsigned char header_fw_array[] =
-{
-
-};
\ No newline at end of file +/* + * make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled + * define your own firmware array here +*/ +const unsigned char header_fw_array[] = { +}; diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c index f564a6b3aaed..29459b6d9ad1 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c @@ -1,21 +1,22 @@ /* drivers/input/touchscreen/gt9xx_update.c - * + * * 2010 - 2012 Goodix Technology. - * + * Copyright (c) 2013-2016, 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 as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. - * + * * Latest Version:1.6 * Author: andrew@goodix.com - * Revision Record: + * Revision Record: * V1.0: * first release. By Andrew, 2012/08/31 * V1.2: @@ -25,7 +26,7 @@ * 2. modify enter_update_mode; * 3. add update file cal checksum. * By Andrew, 2012/12/12 - * V1.6: + * V1.6: * 1. replace guitar_client with i2c_connect_client; * 2. support firmware header array update. * By Meta, 2013/03/11 @@ -47,8 +48,8 @@ #define UPDATE_FILE_PATH_2 "/data/_goodix_update_.bin" #define UPDATE_FILE_PATH_1 "/sdcard/_goodix_update_.bin" -#define CONFIG_FILE_PATH_1 "/data/_goodix_config_.cfg" -#define CONFIG_FILE_PATH_2 "/sdcard/_goodix_config_.cfg" +#define CONFIG_FILE_PATH_1 "/data/_goodix_config_.cfg" +#define CONFIG_FILE_PATH_2 "/sdcard/_goodix_config_.cfg" #define FW_HEAD_LENGTH 14 #define FW_SECTION_LENGTH 0x2000 @@ -73,39 +74,27 @@ #define SUCCESS 1 #pragma pack(1) -typedef struct -{ - u8 hw_info[4]; //hardware info// - u8 pid[8]; //product id // - u16 vid; //version id // -}st_fw_head; +struct { + u8 hw_info[4]; /* hardware info */ + u8 pid[8]; /* product id */ + u16 vid; /* version id */ +} st_fw_head; #pragma pack() -typedef struct -{ - u8 force_update; - u8 fw_flag; - struct file *file; - struct file *cfg_file; - st_fw_head ic_fw_msg; - mm_segment_t old_fs; -}st_update_msg; +struct { + u8 force_update; + u8 fw_flag; + struct file *file; + struct file *cfg_file; + st_fw_head ic_fw_msg; + mm_segment_t old_fs; +} st_update_msg; st_update_msg update_msg; u16 show_len; u16 total_len; -u8 got_file_flag = 0; -u8 searching_file = 0; -extern u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]; -extern void gtp_reset_guitar(struct i2c_client *client, s32 ms); -extern s32 gtp_send_cfg(struct i2c_client *client); -extern struct i2c_client * i2c_connect_client; -extern void gtp_irq_enable(struct goodix_ts_data *ts); -extern void gtp_irq_disable(struct goodix_ts_data *ts); -extern s32 gtp_i2c_read_dbl_check(struct i2c_client *, u16, u8 *, int); -#if GTP_ESD_PROTECT -extern void gtp_esd_switch(struct i2c_client *, s32); -#endif +u8 got_file_flag; +u8 searching_file; /******************************************************* Function: Read data from the i2c slave device. @@ -115,37 +104,37 @@ Input: buf[2~len-1]: read data buffer. len: GTP_ADDR_LENGTH + read bytes count Output: - numbers of i2c_msgs to transfer: + numbers of i2c_msgs to transfer: 2: succeed, otherwise: failed *********************************************************/ s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len) { - struct i2c_msg msgs[2]; - s32 ret=-1; - s32 retries = 0; - - GTP_DEBUG_FUNC(); - - msgs[0].flags = !I2C_M_RD; - msgs[0].addr = client->addr; - msgs[0].len = GTP_ADDR_LENGTH; - msgs[0].buf = &buf[0]; - //msgs[0].scl_rate = 300 * 1000; // for Rockchip - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = client->addr; - msgs[1].len = len - GTP_ADDR_LENGTH; - msgs[1].buf = &buf[GTP_ADDR_LENGTH]; - //msgs[1].scl_rate = 300 * 1000; - - while(retries < 5) - { - ret = i2c_transfer(client->adapter, msgs, 2); - if(ret == 2)break; - retries++; - } - - return ret; + struct i2c_msg msgs[2]; + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = client->addr; + msgs[0].len = GTP_ADDR_LENGTH; + msgs[0].buf = &buf[0]; + /* msgs[0].scl_rate = 300 * 1000; (for Rockchip) */ + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len - GTP_ADDR_LENGTH; + msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + /* msgs[1].scl_rate = 300 * 1000; */ + + while (retries < 5) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) + break; + retries++; + } + + return ret; } /******************************************************* @@ -157,1774 +146,1643 @@ Input: buf[2~len-1]: data buffer len: GTP_ADDR_LENGTH + write bytes count Output: - numbers of i2c_msgs to transfer: - 1: succeed, otherwise: failed + numbers of i2c_msgs to transfer: + 1: succeed, otherwise: failed *********************************************************/ -s32 gup_i2c_write(struct i2c_client *client,u8 *buf,s32 len) +s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len) { - struct i2c_msg msg; - s32 ret=-1; - s32 retries = 0; - - GTP_DEBUG_FUNC(); - - msg.flags = !I2C_M_RD; - msg.addr = client->addr; - msg.len = len; - msg.buf = buf; - //msg.scl_rate = 300 * 1000; // for Rockchip - - while(retries < 5) - { - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret == 1)break; - retries++; - } - - return ret; + struct i2c_msg msg; + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = len; + msg.buf = buf; + /* msg.scl_rate = 300 * 1000; (for Rockchip) */ + + while (retries < 5) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) + break; + retries++; + } + + return ret; } static s32 gup_init_panel(struct goodix_ts_data *ts) { - s32 ret = 0; - s32 i = 0; - u8 check_sum = 0; - u8 opr_buf[16]; - u8 sensor_id = 0; - - u8 cfg_info_group1[] = CTP_CFG_GROUP1; - u8 cfg_info_group2[] = CTP_CFG_GROUP2; - u8 cfg_info_group3[] = CTP_CFG_GROUP3; - u8 cfg_info_group4[] = CTP_CFG_GROUP4; - u8 cfg_info_group5[] = CTP_CFG_GROUP5; - u8 cfg_info_group6[] = CTP_CFG_GROUP6; - u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, - cfg_info_group4, cfg_info_group5, cfg_info_group6}; - u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1), - CFG_GROUP_LEN(cfg_info_group2), - CFG_GROUP_LEN(cfg_info_group3), - CFG_GROUP_LEN(cfg_info_group4), - CFG_GROUP_LEN(cfg_info_group5), - CFG_GROUP_LEN(cfg_info_group6)}; - - if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && - (!cfg_info_len[3]) && (!cfg_info_len[4]) && - (!cfg_info_len[5])) - { - sensor_id = 0; - } - else - { - ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); - if (SUCCESS == ret) - { - if (sensor_id >= 0x06) - { - GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); - return -1; - } - } - else - { - GTP_ERROR("Failed to get sensor_id, No config sent!"); - return -1; - } - } - - GTP_DEBUG("Sensor_ID: %d", sensor_id); - - ts->gtp_cfg_len = cfg_info_len[sensor_id]; - - if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) - { - GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id); - return -1; - } - - ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); - - if (ret == SUCCESS) - { - GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version: %d", sensor_id+1, - send_cfg_buf[sensor_id][0], opr_buf[0]); - - send_cfg_buf[sensor_id][0] = opr_buf[0]; - ts->fixed_cfg = 0; - /* - if (opr_buf[0] < 90) - { - grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version - send_cfg_buf[sensor_id][0] = 0x00; - ts->fixed_cfg = 0; - } - else // treated as fixed config, not send config - { - GTP_INFO("Ic fixed config with config version(%d)", opr_buf[0]); - ts->fixed_cfg = 1; - }*/ - } - else - { - GTP_ERROR("Failed to get ic config version!No config sent!"); - return -1; - } - - memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); - memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len); - - GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", - ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type); - - config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; - config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); - config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; - config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); - - if (GTP_INT_TRIGGER == 0) //RISING - { - config[TRIGGER_LOC] &= 0xfe; - } - else if (GTP_INT_TRIGGER == 1) //FALLING - { - config[TRIGGER_LOC] |= 0x01; - } - - check_sum = 0; - for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) - { - check_sum += config[i]; - } - config[ts->gtp_cfg_len] = (~check_sum) + 1; - - GTP_DEBUG_FUNC(); - ret = gtp_send_cfg(ts->client); - if (ret < 0) - { - GTP_ERROR("Send config error."); - } - - msleep(10); - return 0; + s32 ret = 0; + s32 i = 0; + u8 check_sum = 0; + u8 opr_buf[16]; + u8 sensor_id = 0; + + u8 cfg_info_group1[] = CTP_CFG_GROUP1; + u8 cfg_info_group2[] = CTP_CFG_GROUP2; + u8 cfg_info_group3[] = CTP_CFG_GROUP3; + u8 cfg_info_group4[] = CTP_CFG_GROUP4; + u8 cfg_info_group5[] = CTP_CFG_GROUP5; + u8 cfg_info_group6[] = CTP_CFG_GROUP6; + u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, + cfg_info_group4, cfg_info_group5, cfg_info_group6}; + u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1), + CFG_GROUP_LEN(cfg_info_group2), + CFG_GROUP_LEN(cfg_info_group3), + CFG_GROUP_LEN(cfg_info_group4), + CFG_GROUP_LEN(cfg_info_group5), + CFG_GROUP_LEN(cfg_info_group6)}; + + if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && + (!cfg_info_len[3]) && (!cfg_info_len[4]) && + (!cfg_info_len[5])) { + sensor_id = 0; + } else { + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, + &sensor_id, 1); + if (ret == SUCCESS) { + if (sensor_id >= 0x06) { + GTP_ERROR( + "Invalid sensor_id(0x%02X), No Config Sent!", + sensor_id); + return -EINVAL; + } + } else { + GTP_ERROR("Failed to get sensor_id, No config sent!"); + return -EINVAL; + } + } + + GTP_DEBUG("Sensor_ID: %d", sensor_id); + + ts->gtp_cfg_len = cfg_info_len[sensor_id]; + + if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) { + GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG", + " GROUP! NO Config Sent! You need to check you header", + " file CFG_GROUP section!", sensor_id); + return -EINVAL; + } + + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, + &opr_buf[0], 1); + + if (ret == SUCCESS) { + GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version:", + " %d", sensor_id+1, send_cfg_buf[sensor_id][0], opr_buf[0]); + + send_cfg_buf[sensor_id][0] = opr_buf[0]; + ts->fixed_cfg = 0; + /* + * if (opr_buf[0] < 90) { + * grp_cfg_version = send_cfg_buf[sensor_id][0]; + * *** backup group config version *** + * send_cfg_buf[sensor_id][0] = 0x00; + * ts->fixed_cfg = 0; + * } else { *** treated as fixed config, not send config *** + * GTP_INFO("Ic fixed config with config version(%d)", + * opr_buf[0]); + * ts->fixed_cfg = 1; + * } + */ + } else { + GTP_ERROR("Failed to get ic config version!No config sent!"); + return -EINVAL; + } + + memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], + ts->gtp_cfg_len); + + GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", + ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type); + + config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; + config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); + config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; + config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); + + if (GTP_INT_TRIGGER == 0) /* RISING */ + config[TRIGGER_LOC] &= 0xfe; + else if (GTP_INT_TRIGGER == 1) /* FALLING */ + config[TRIGGER_LOC] |= 0x01; + + check_sum = 0; + for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) + check_sum += config[i]; + + config[ts->gtp_cfg_len] = (~check_sum) + 1; + + GTP_DEBUG_FUNC(); + ret = gtp_send_cfg(ts->client); + if (ret < 0) + GTP_ERROR("Send config error."); + + msleep(20); + return 0; } -static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8* msg, s32 len) +static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len) { - s32 i = 0; - - msg[0] = (addr >> 8) & 0xff; - msg[1] = addr & 0xff; - - for (i = 0; i < 5; i++) - { - if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0) - { - break; - } - } - - if (i >= 5) - { - GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]); - return FAIL; - } - - return SUCCESS; + s32 i = 0; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + + for (i = 0; i < 5; i++) + if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0) + break; + + if (i >= 5) { + GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; } static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val) { - s32 i = 0; - u8 msg[3]; - - msg[0] = (addr >> 8) & 0xff; - msg[1] = addr & 0xff; - msg[2] = val; - - for (i = 0; i < 5; i++) - { - if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) - { - break; - } - } - - if (i >= 5) - { - GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]); - return FAIL; - } - - return SUCCESS; + s32 i = 0; + u8 msg[3]; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + msg[2] = val; + + for (i = 0; i < 5; i++) + if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) + break; + + if (i >= 5) { + GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; } static u8 gup_get_ic_fw_msg(struct i2c_client *client) { - s32 ret = -1; - u8 retry = 0; - u8 buf[16]; - u8 i; - - // step1:get hardware info - ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, &buf[GTP_ADDR_LENGTH], 4); - if (FAIL == ret) - { - GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit"); - return FAIL; - } - - // buf[2~5]: 00 06 90 00 - // hw_info: 00 90 06 00 - for(i=0; i<4; i++) - { - update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i]; - } - GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1], - update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]); - // step2:get firmware message - for(retry=0; retry<2; retry++) - { - ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1); - if(FAIL == ret) - { - GTP_ERROR("Read firmware message fail."); - return ret; - } - - update_msg.force_update = buf[GTP_ADDR_LENGTH]; - if((0xBE != update_msg.force_update)&&(!retry)) - { - GTP_INFO("The check sum in ic is error."); - GTP_INFO("The IC will be updated by force."); - continue; - } - break; - } - GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update); - - // step3:get pid & vid - ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, &buf[GTP_ADDR_LENGTH], 6); - if (FAIL == ret) - { - GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit"); - return FAIL; - } - - memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid)); - memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4); - GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid); - - //GT9XX PID MAPPING - /*|-----FLASH-----RAM-----| - |------918------918-----| - |------968------968-----| - |------913------913-----| - |------913P-----913P----| - |------927------927-----| - |------927P-----927P----| - |------9110-----9110----| - |------9110P----9111----|*/ - if(update_msg.ic_fw_msg.pid[0] != 0) - { - if(!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) - { - GTP_DEBUG("IC Mapping Product id:%s", update_msg.ic_fw_msg.pid); - memcpy(update_msg.ic_fw_msg.pid, "9110P", 5); - } - } - - update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH+4] + (buf[GTP_ADDR_LENGTH+5]<<8); - GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid); - - return SUCCESS; + s32 ret = -1; + u8 retry = 0; + u8 buf[16]; + u8 i; + + /* step1:get hardware info */ + ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, + &buf[GTP_ADDR_LENGTH], 4); + if (ret == FAIL) { + GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit"); + return FAIL; + } + + /* buf[2~5]: 00 06 90 00 */ + /* hw_info: 00 90 06 00 */ + for (i = 0; i < 4; i++) + update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i]; + + GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", + update_msg.ic_fw_msg.hw_info[0], + update_msg.ic_fw_msg.hw_info[1], + update_msg.ic_fw_msg.hw_info[2], + update_msg.ic_fw_msg.hw_info[3]); + + /* step2:get firmware message */ + for (retry = 0; retry < 2; retry++) { + ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1); + if (ret == FAIL) { + GTP_ERROR("Read firmware message fail."); + return ret; + } + + update_msg.force_update = buf[GTP_ADDR_LENGTH]; + if ((update_msg.force_update != 0xBE) && (!retry)) { + GTP_INFO("The check sum in ic is error."); + GTP_INFO("The IC will be updated by force."); + continue; + } + break; + } + GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update); + + /* step3:get pid & vid */ + ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, + &buf[GTP_ADDR_LENGTH], 6); + if (ret == FAIL) { + GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit"); + return FAIL; + } + + memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid)); + memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4); + GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid); + + /* GT9XX PID MAPPING + * |-----FLASH-----RAM-----| + * |------918------918-----| + * |------968------968-----| + * |------913------913-----| + * |------913P-----913P----| + * |------927------927-----| + * |------927P-----927P----| + * |------9110-----9110----| + * |------9110P----9111----| + */ + if (update_msg.ic_fw_msg.pid[0] != 0) { + if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) { + GTP_DEBUG("IC Mapping Product id:%s", + update_msg.ic_fw_msg.pid); + memcpy(update_msg.ic_fw_msg.pid, "9110P", 5); + } + } + + update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] + + (buf[GTP_ADDR_LENGTH + 5] << 8); + GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid); + + return SUCCESS; } s32 gup_enter_update_mode(struct i2c_client *client) { - s32 ret = -1; - s32 retry = 0; - u8 rd_buf[3]; - - //step1:RST output low last at least 2ms - GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); - msleep(2); - - //step2:select I2C slave addr,INT:0--0xBA;1--0x28. - GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14)); - msleep(2); - - //step3:RST output high reset guitar - GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); - - //20121211 modify start - msleep(5); - while(retry++ < 200) - { - //step4:Hold ss51 & dsp - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if(ret <= 0) - { - GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); - continue; - } - - //step5:Confirm hold - ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1); - if(ret <= 0) - { - GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); - continue; - } - if(0x0C == rd_buf[GTP_ADDR_LENGTH]) - { - GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS"); - break; - } - GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", rd_buf[GTP_ADDR_LENGTH]); - } - if(retry >= 200) - { - GTP_ERROR("Enter update Hold ss51 failed."); - return FAIL; - } - - //step6:DSP_CK and DSP_ALU_CK PowerOn - ret = gup_set_ic_msg(client, 0x4010, 0x00); - - //20121211 modify end - return ret; + s32 ret = -1; + s32 retry = 0; + u8 rd_buf[3]; + + /* step1:RST output low last at least 2ms */ + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(20); + + /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */ + GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14)); + msleep(20); + + /* step3:RST output high reset guitar */ + GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); + + /* 20121211 modify start */ + msleep(20); + while (retry++ < 200) { + /* step4:Hold ss51 & dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if (ret <= 0) { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + + /* step5:Confirm hold */ + ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1); + if (ret <= 0) { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + if (rd_buf[GTP_ADDR_LENGTH] == 0x0C) { + GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS"); + break; + } + GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", + rd_buf[GTP_ADDR_LENGTH]); + } + if (retry >= 200) { + GTP_ERROR("Enter update Hold ss51 failed."); + return FAIL; + } + + /* step6:DSP_CK and DSP_ALU_CK PowerOn */ + ret = gup_set_ic_msg(client, 0x4010, 0x00); + + /* 20121211 modify end */ + return ret; } void gup_leave_update_mode(void) { - GTP_GPIO_AS_INT(GTP_INT_PORT); - - GTP_DEBUG("[leave_update_mode]reset chip."); - gtp_reset_guitar(i2c_connect_client, 20); + GTP_GPIO_AS_INT(GTP_INT_PORT); + + GTP_DEBUG("[leave_update_mode]reset chip."); + gtp_reset_guitar(i2c_connect_client, 20); } -// Get the correct nvram data -// The correct conditions: -// 1. the hardware info is the same -// 2. the product id is the same -// 3. the firmware version in update file is greater than the firmware version in ic -// or the check sum in ic is wrong -/* Update Conditions: - 1. Same hardware info - 2. Same PID - 3. File PID > IC PID - Force Update Conditions: - 1. Wrong ic firmware checksum - 2. INVALID IC PID or VID - 3. IC PID == 91XX || File PID == 91XX -*/ +/* Get the correct nvram data + * The correct conditions: + * 1. the hardware info is the same + * 2. the product id is the same + * 3. the firmware version in update file is greater than the firmware + * version in ic or the check sum in ic is wrong + + * Update Conditions: + * 1. Same hardware info + * 2. Same PID + * 3. File PID > IC PID + + * Force Update Conditions: + * 1. Wrong ic firmware checksum + * 2. INVALID IC PID or VID + * 3. IC PID == 91XX || File PID == 91XX + */ static u8 gup_enter_update_judge(st_fw_head *fw_head) { - u16 u16_tmp; - s32 i = 0; - - u16_tmp = fw_head->vid; - fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8); - - GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]); - GTP_DEBUG("FILE PID:%s", fw_head->pid); - GTP_DEBUG("FILE VID:%04x", fw_head->vid); - - GTP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1], - update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]); - GTP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid); - GTP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid); - - //First two conditions - if ( !memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, sizeof(update_msg.ic_fw_msg.hw_info))) - { - GTP_DEBUG("Get the same hardware info."); - if( update_msg.force_update != 0xBE ) - { - GTP_INFO("FW chksum error,need enter update."); - return SUCCESS; - } - - // 20130523 start - if (strlen(update_msg.ic_fw_msg.pid) < 3) - { - GTP_INFO("Illegal IC pid, need enter update"); - return SUCCESS; - } - else - { - for (i = 0; i < 3; i++) - { - if ((update_msg.ic_fw_msg.pid[i] < 0x30) || (update_msg.ic_fw_msg.pid[i] > 0x39)) - { - GTP_INFO("Illegal IC pid, out of bound, need enter update"); - return SUCCESS; - } - } - } - // 20130523 end - - - if (( !memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, (strlen(fw_head->pid)<3?3:strlen(fw_head->pid))))|| - (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4))|| - (!memcmp(fw_head->pid, "91XX", 4))) - { - if(!memcmp(fw_head->pid, "91XX", 4)) - { - GTP_DEBUG("Force none same pid update mode."); - } - else - { - GTP_DEBUG("Get the same pid."); - } - //The third condition - if (fw_head->vid > update_msg.ic_fw_msg.vid) - { - - GTP_INFO("Need enter update."); - return SUCCESS; - } - GTP_ERROR("Don't meet the third condition."); - GTP_ERROR("File VID <= Ic VID, update aborted!"); - } - else - { - GTP_ERROR("File PID != Ic PID, update aborted!"); - } - } - else - { - GTP_ERROR("Different Hardware, update aborted!"); - } - return FAIL; + u16 u16_tmp; + s32 i = 0; + + u16_tmp = fw_head->vid; + fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8); + + GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], + fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]); + TP_DEBUG("FILE PID:%s", fw_head->pid); + TP_DEBUG("FILE VID:%04x", fw_head->vid); + + TP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", + update_msg.ic_fw_msg.hw_info[0], + update_msg.ic_fw_msg.hw_info[1], + update_msg.ic_fw_msg.hw_info[2], + update_msg.ic_fw_msg.hw_info[3]); + TP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid); + TP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid); + + /* First two conditions */ + if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, + sizeof(update_msg.ic_fw_msg.hw_info))) { + GTP_DEBUG("Get the same hardware info."); + if (update_msg.force_update != 0xBE) { + GTP_INFO("FW chksum error,need enter update."); + return SUCCESS; + } + + /* 20130523 start */ + if (strlen(update_msg.ic_fw_msg.pid) < 3) { + GTP_INFO("Illegal IC pid, need enter update"); + return SUCCESS; + } + for (i = 0; i < 3; i++) { + if ((update_msg.ic_fw_msg.pid[i] < 0x30) || + (update_msg.ic_fw_msg.pid[i] > 0x39)) { + GTP_INFO("Illegal IC pid, out of ", + "bound, need enter update"); + return SUCCESS; + } + } + /* 20130523 end */ + + if ((!memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, + (strlen(fw_head->pid) < 3 ? 3 : strlen(fw_head->pid)))) || + (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) || + (!memcmp(fw_head->pid, "91XX", 4))) { + if (!memcmp(fw_head->pid, "91XX", 4)) + GTP_DEBUG("Force none same pid update mode."); + else + GTP_DEBUG("Get the same pid."); + + /* The third condition */ + if (fw_head->vid > update_msg.ic_fw_msg.vid) { + GTP_INFO("Need enter update."); + return SUCCESS; + } + GTP_ERROR("Don't meet the third condition."); + GTP_ERROR("File VID <= Ic VID, update aborted!"); + } else { + GTP_ERROR("File PID != Ic PID, update aborted!"); + } + } else { + GTP_ERROR("Different Hardware, update aborted!"); + } + + return FAIL; } static u8 ascii2hex(u8 a) { - s8 value = 0; - - if(a >= '0' && a <= '9') - { - value = a - '0'; - } - else if(a >= 'A' && a <= 'F') - { - value = a - 'A' + 0x0A; - } - else if(a >= 'a' && a <= 'f') - { - value = a - 'a' + 0x0A; - } - else - { - value = 0xff; - } - - return value; + s8 value = 0; + + if (a >= '0' && a <= '9') + value = a - '0'; + else if (a >= 'A' && a <= 'F') + value = a - 'A' + 0x0A; + else if (a >= 'a' && a <= 'f') + value = a - 'a' + 0x0A; + else + value = 0xff; + + return value; } static s8 gup_update_config(struct i2c_client *client) { - s32 file_len = 0; - s32 ret = 0; - s32 i = 0; - s32 file_cfg_len = 0; - s32 chip_cfg_len = 0; - s32 count = 0; - u8 *buf; - u8 *pre_buf; - u8 *file_config; - //u8 checksum = 0; - u8 pid[8]; - - if(NULL == update_msg.cfg_file) - { - GTP_ERROR("[update_cfg]No need to upgrade config!"); - return FAIL; - } - file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_END); - - ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6); - if(FAIL == ret) - { - GTP_ERROR("[update_cfg]Read product id & version id fail."); - return FAIL; - } - pid[5] = '\0'; - GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]); - - chip_cfg_len = 186; - if(!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) || - !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) || - !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) - { - chip_cfg_len = 228; - } - GTP_DEBUG("[update_cfg]config file len:%d", file_len); - GTP_DEBUG("[update_cfg]need config len:%d",chip_cfg_len); - if((file_len+5) < chip_cfg_len*5) - { - GTP_ERROR("Config length error"); - return -1; - } - - buf = (u8*)kzalloc(file_len, GFP_KERNEL); - pre_buf = (u8*)kzalloc(file_len, GFP_KERNEL); - file_config = (u8*)kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL); - update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET); - - GTP_DEBUG("[update_cfg]Read config from file."); - ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file, (char*)pre_buf, file_len, &update_msg.cfg_file->f_pos); - if(ret<0) - { - GTP_ERROR("[update_cfg]Read config file failed."); - goto update_cfg_file_failed; - } - - GTP_DEBUG("[update_cfg]Delete illgal charactor."); - for(i=0,count=0; i<file_len; i++) - { - if (pre_buf[i] == ' ' || pre_buf[i] == '\r' || pre_buf[i] == '\n') - { - continue; - } - buf[count++] = pre_buf[i]; - } - - GTP_DEBUG("[update_cfg]Ascii to hex."); - file_config[0] = GTP_REG_CONFIG_DATA >> 8; - file_config[1] = GTP_REG_CONFIG_DATA & 0xff; - for(i=0,file_cfg_len=GTP_ADDR_LENGTH; i<count; i+=5) - { - if((buf[i]=='0') && ((buf[i+1]=='x') || (buf[i+1]=='X'))) - { - u8 high,low; - high = ascii2hex(buf[i+2]); - low = ascii2hex(buf[i+3]); - - if((high == 0xFF) || (low == 0xFF)) - { - ret = 0; - GTP_ERROR("[update_cfg]Illegal config file."); - goto update_cfg_file_failed; - } - file_config[file_cfg_len++] = (high<<4) + low; - } - else - { - ret = 0; - GTP_ERROR("[update_cfg]Illegal config file."); - goto update_cfg_file_failed; - } - } - -// //cal checksum -// for(i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++) -// { -// checksum += file_config[i]; -// } -// file_config[chip_cfg_len] = (~checksum) + 1; -// file_config[chip_cfg_len+1] = 0x01; - - GTP_DEBUG("config:"); - GTP_DEBUG_ARRAY(file_config+2, file_cfg_len); - - i = 0; - while(i++ < 5) - { - ret = gup_i2c_write(client, file_config, file_cfg_len); - if(ret > 0) - { - GTP_INFO("[update_cfg]Send config SUCCESS."); - break; - } - GTP_ERROR("[update_cfg]Send config i2c error."); - } - + s32 file_len = 0; + s32 ret = 0; + s32 i = 0; + s32 file_cfg_len = 0; + s32 chip_cfg_len = 0; + s32 count = 0; + u8 *buf; + u8 *pre_buf; + u8 *file_config; + /* u8 checksum = 0; */ + u8 pid[8]; + + if (update_msg.cfg_file == NULL) { + GTP_ERROR("[update_cfg]No need to upgrade config!"); + return FAIL; + } + file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, + 0, SEEK_END); + + ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6); + if (ret == FAIL) { + GTP_ERROR("[update_cfg]Read product id & version id fail."); + return FAIL; + } + pid[5] = '\0'; + GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]); + + chip_cfg_len = 186; + if (!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) || + !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) || + !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) { + chip_cfg_len = 228; + } + GTP_DEBUG("[update_cfg]config file len:%d", file_len); + GTP_DEBUG("[update_cfg]need config len:%d", chip_cfg_len); + if ((file_len+5) < chip_cfg_len*5) { + GTP_ERROR("Config length error"); + return -EINVAL; + } + + buf = kzalloc(file_len, GFP_KERNEL); + pre_buf = kzalloc(file_len, GFP_KERNEL); + file_config = kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL); + update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET); + + GTP_DEBUG("[update_cfg]Read config from file."); + ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file, + (char *)pre_buf, file_len, &update_msg.cfg_file->f_pos); + if (ret < 0) { + GTP_ERROR("[update_cfg]Read config file failed."); + goto update_cfg_file_failed; + } + + GTP_DEBUG("[update_cfg]Delete illegal character."); + for (i = 0, count = 0; i < file_len; i++) { + if (pre_buf[i] == ' ' || pre_buf[i] == '\r' + || pre_buf[i] == '\n') + continue; + buf[count++] = pre_buf[i]; + } + + GTP_DEBUG("[update_cfg]Ascii to hex."); + file_config[0] = GTP_REG_CONFIG_DATA >> 8; + file_config[1] = GTP_REG_CONFIG_DATA & 0xff; + for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i + = 5) { + if ((buf[i] == '0') && ((buf[i+1] == 'x') || + (buf[i+1] == 'X'))) { + u8 high, low; + + high = ascii2hex(buf[i+2]); + low = ascii2hex(buf[i+3]); + + if ((high == 0xFF) || (low == 0xFF)) { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + file_config[file_cfg_len++] = (high<<4) + low; + } else { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + } + + /* cal checksum */ + /* for (i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++) + * checksum += file_config[i]; + * file_config[chip_cfg_len] = (~checksum) + 1; + * file_config[chip_cfg_len+1] = 0x01; + */ + + GTP_DEBUG("config:"); + GTP_DEBUG_ARRAY(file_config+2, file_cfg_len); + + i = 0; + while (i++ < 5) { + ret = gup_i2c_write(client, file_config, file_cfg_len); + if (ret > 0) { + GTP_INFO("[update_cfg]Send config SUCCESS."); + break; + } + GTP_ERROR("[update_cfg]Send config i2c error."); + } + update_cfg_file_failed: - kfree(pre_buf); - kfree(buf); - kfree(file_config); - return ret; + kfree(pre_buf); + kfree(buf); + kfree(file_config); + return ret; } #if GTP_HEADER_FW_UPDATE static u8 gup_check_fs_mounted(char *path_name) { - struct path root_path; - struct path path; - int err; - err = kern_path("/", LOOKUP_FOLLOW, &root_path); - - if (err) - { - GTP_DEBUG("\"/\" NOT Mounted: %d", err); - return FAIL; - } - err = kern_path(path_name, LOOKUP_FOLLOW, &path); - - if (err) - { - GTP_DEBUG("/data/ NOT Mounted: %d", err); - return FAIL; - } - - return SUCCESS; - - /* - if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) - { - //-- not mounted - return FAIL; - } - else - { - return SUCCESS; - }*/ - + struct path root_path; + struct path path; + int err; + + err = kern_path("/", LOOKUP_FOLLOW, &root_path); + + if (err) { + GTP_DEBUG("\"/\" NOT Mounted: %d", err); + return FAIL; + } + err = kern_path(path_name, LOOKUP_FOLLOW, &path); + + if (err) { + GTP_DEBUG("/data/ NOT Mounted: %d", err); + return FAIL; + } + + return SUCCESS; + + /* if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) + * return FAIL; + * else + * return SUCCESS; + */ } #endif -static u8 gup_check_update_file(struct i2c_client *client, st_fw_head* fw_head, u8* path) + +static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head, + u8 *path) { - s32 ret = 0; - s32 i = 0; - s32 fw_checksum = 0; - u8 buf[FW_HEAD_LENGTH]; - - if (path) - { - GTP_DEBUG("Update File path:%s, %d", path, strlen(path)); - update_msg.file = filp_open(path, O_RDONLY, 0); - - if (IS_ERR(update_msg.file)) - { - GTP_ERROR("Open update file(%s) error!", path); - return FAIL; - } - } - else - { + s32 ret = 0; + s32 i = 0; + s32 fw_checksum = 0; + u8 buf[FW_HEAD_LENGTH]; + + if (path) { + GTP_DEBUG("Update File path:%s, %d", path, strlen(path)); + update_msg.file = file_open(path, O_RDONLY, 0); + + if (IS_ERR(update_msg.file)) { + GTP_ERROR("Open update file(%s) error!", path); + return FAIL; + } + } else { #if GTP_HEADER_FW_UPDATE - for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++) - { - GTP_DEBUG("Waiting for /data mounted [%d]", i); - - if (gup_check_fs_mounted("/data") == SUCCESS) - { - GTP_DEBUG("/data Mounted!"); - break; - } - msleep(3000); - } - if (i >= (GUP_SEARCH_FILE_TIMES)) - { - GTP_ERROR("Wait for /data mounted timeout!"); - return FAIL; - } - - // update config - update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_1, O_RDONLY, 0); - if (IS_ERR(update_msg.cfg_file)) - { - GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1); - } - else - { - GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1); - ret = gup_update_config(client); - if(ret <= 0) - { - GTP_ERROR("Update config failed."); - } - filp_close(update_msg.cfg_file, NULL); - } - - if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH)) - { - GTP_ERROR("INVALID header_fw_array, check your gt9xx_firmware.h file!"); - return FAIL; - } - update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_CREAT | O_RDWR, 0666); - if ((IS_ERR(update_msg.file))) - { - GTP_ERROR("Failed to Create file: %s for fw_header!", UPDATE_FILE_PATH_2); - return FAIL; - } - update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); - update_msg.file->f_op->write(update_msg.file, (char *)header_fw_array, sizeof(header_fw_array), &update_msg.file->f_pos); - filp_close(update_msg.file, NULL); - update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_RDONLY, 0); + for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++) { + GTP_DEBUG("Waiting for /data mounted [%d]", i); + + if (gup_check_fs_mounted("/data") == SUCCESS) { + GTP_DEBUG("/data Mounted!"); + break; + } + msleep(3000); + } + if (i >= (GUP_SEARCH_FILE_TIMES)) { + GTP_ERROR("Wait for /data mounted timeout!"); + return FAIL; + } + + /* update config */ + update_msg.cfg_file = file_open(CONFIG_FILE_PATH_1, + O_RDONLY, 0); + + if (IS_ERR(update_msg.cfg_file)) { + GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1); + } else { + GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1); + ret = gup_update_config(client); + if (ret <= 0) + GTP_ERROR("Update config failed."); + filp_close(update_msg.cfg_file, NULL); + } + + if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH + *4 + FW_DSP_ISP_LENGTH+FW_DSP_LENGTH + FW_BOOT_LENGTH)) { + GTP_ERROR("INVALID header_fw_array, check your ", + "gt9xx_firmware.h file!"); + return FAIL; + } + update_msg.file = file_open(UPDATE_FILE_PATH_2, O_CREAT | + O_RDWR, 0666); + if ((IS_ERR(update_msg.file))) { + GTP_ERROR("Failed to Create file: %s for fw_header!", + UPDATE_FILE_PATH_2); + return FAIL; + } + update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); + update_msg.file->f_op->write(update_msg.file, + (char *)header_fw_array, sizeof(header_fw_array), + &update_msg.file->f_pos); + file_close(update_msg.file, NULL); + update_msg.file = file_open(UPDATE_FILE_PATH_2, O_RDONLY, 0); #else - u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), sizeof(UPDATE_FILE_PATH_2)); - u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), sizeof(CONFIG_FILE_PATH_2)); - u8 *search_update_path = (u8*)kzalloc(fp_len, GFP_KERNEL); - u8 *search_cfg_path = (u8*)kzalloc(cfp_len, GFP_KERNEL); - //Begin to search update file,the config file & firmware file must be in the same path,single or double. - searching_file = 1; - for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) - { - if (searching_file == 0) - { - kfree(search_update_path); - kfree(search_cfg_path); - GTP_INFO(".bin/.cfg update file search forcely terminated!"); - return FAIL; - } - if(i%2) - { - memcpy(search_update_path, UPDATE_FILE_PATH_1, sizeof(UPDATE_FILE_PATH_1)); - memcpy(search_cfg_path, CONFIG_FILE_PATH_1, sizeof(CONFIG_FILE_PATH_1)); - } - else - { - memcpy(search_update_path, UPDATE_FILE_PATH_2, sizeof(UPDATE_FILE_PATH_2)); - memcpy(search_cfg_path, CONFIG_FILE_PATH_2, sizeof(CONFIG_FILE_PATH_2)); - } - - if(!(got_file_flag&0x0F)) - { - update_msg.file = filp_open(search_update_path, O_RDONLY, 0); - if(!IS_ERR(update_msg.file)) - { - GTP_DEBUG("Find the bin file"); - got_file_flag |= 0x0F; - } - } - if(!(got_file_flag&0xF0)) - { - update_msg.cfg_file = filp_open(search_cfg_path, O_RDONLY, 0); - if(!IS_ERR(update_msg.cfg_file)) - { - GTP_DEBUG("Find the cfg file"); - got_file_flag |= 0xF0; - } - } - - if(got_file_flag) - { - if(got_file_flag == 0xFF) - { - break; - } - else - { - i += 4; - } - } - GTP_DEBUG("%3d:Searching %s %s file...", i, (got_file_flag&0x0F)?"":"bin", (got_file_flag&0xF0)?"":"cfg"); - msleep(3000); - } - searching_file = 0; - kfree(search_update_path); - kfree(search_cfg_path); - - if(!got_file_flag) - { - GTP_ERROR("Can't find update file."); - goto load_failed; - } - - if(got_file_flag&0xF0) - { - GTP_DEBUG("Got the update config file."); - ret = gup_update_config(client); - if(ret <= 0) - { - GTP_ERROR("Update config failed."); - } - filp_close(update_msg.cfg_file, NULL); - msleep(500); //waiting config to be stored in FLASH. - } - if(got_file_flag&0x0F) - { - GTP_DEBUG("Got the update firmware file."); - } - else - { - GTP_ERROR("No need to upgrade firmware."); - goto load_failed; - } + u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), + sizeof(UPDATE_FILE_PATH_2)); + u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), + sizeof(CONFIG_FILE_PATH_2)); + u8 *search_update_path = kzalloc(fp_len, GFP_KERNEL); + u8 *search_cfg_path = kzalloc(cfp_len, GFP_KERNEL); + /* Begin to search update file,the config file & firmware + * file must be in the same path,single or double. + */ + searching_file = 1; + for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) { + if (searching_file == 0) { + kfree(search_update_path); + kfree(search_cfg_path); + GTP_INFO(".bin/.cfg update file search ", + "forcely terminated!"); + return FAIL; + } + if (i % 2) { + memcpy(search_update_path, UPDATE_FILE_PATH_1, + sizeof(UPDATE_FILE_PATH_1)); + memcpy(search_cfg_path, CONFIG_FILE_PATH_1, + sizeof(CONFIG_FILE_PATH_1)); + } else { + memcpy(search_update_path, UPDATE_FILE_PATH_2, + sizeof(UPDATE_FILE_PATH_2)); + memcpy(search_cfg_path, CONFIG_FILE_PATH_2, + sizeof(CONFIG_FILE_PATH_2)); + } + + if (!(got_file_flag&0x0F)) { + update_msg.file = file_open(search_update_path, + O_RDONLY, 0); + if (!IS_ERR(update_msg.file)) { + GTP_DEBUG("Find the bin file"); + got_file_flag |= 0x0F; + } + } + if (!(got_file_flag & 0xF0)) { + update_msg.cfg_file = file_open(search_cfg_path, + O_RDONLY, 0); + if (!IS_ERR(update_msg.cfg_file)) { + GTP_DEBUG("Find the cfg file"); + got_file_flag |= 0xF0; + } + } + + if (got_file_flag) { + if (got_file_flag == 0xFF) + break; + i += 4; + } + GTP_DEBUG("%3d:Searching %s %s file...", i, + (got_file_flag & 0x0F) ? "" : "bin", + (got_file_flag & 0xF0) ? "" : "cfg"); + + msleep(3000); + } + + searching_file = 0; + kfree(search_update_path); + kfree(search_cfg_path); + + if (!got_file_flag) { + GTP_ERROR("Can't find update file."); + goto load_failed; + } + + if (got_file_flag & 0xF0) { + GTP_DEBUG("Got the update config file."); + ret = gup_update_config(client); + if (ret <= 0) + GTP_ERROR("Update config failed."); + filp_close(update_msg.cfg_file, NULL); + msleep(500); /* waiting config to be stored in FLASH. */ + } + if (got_file_flag & 0x0F) { + GTP_DEBUG("Got the update firmware file."); + } else { + GTP_ERROR("No need to upgrade firmware."); + goto load_failed; + } #endif - } - - update_msg.old_fs = get_fs(); - set_fs(KERNEL_DS); - - update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); - //update_msg.file->f_pos = 0; - - ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, FW_HEAD_LENGTH, &update_msg.file->f_pos); - if (ret < 0) - { - GTP_ERROR("Read firmware head in update file error."); - goto load_failed; - } - memcpy(fw_head, buf, FW_HEAD_LENGTH); - - //check firmware legality - fw_checksum = 0; - for(i=0; i<FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH; i+=2) - { - u16 temp; - ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, 2, &update_msg.file->f_pos); - if (ret < 0) - { - GTP_ERROR("Read firmware file error."); - goto load_failed; - } - //GTP_DEBUG("BUF[0]:%x", buf[0]); - temp = (buf[0]<<8) + buf[1]; - fw_checksum += temp; - } - - GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF); - if(fw_checksum&0xFFFF) - { - GTP_ERROR("Illegal firmware file."); - goto load_failed; - } - - return SUCCESS; + } + + update_msg.old_fs = get_fs(); + set_fs(KERNEL_DS); + + update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); + /* update_msg.file->f_pos = 0; */ + + ret = update_msg.file->f_op->read(update_msg.file, (char *)buf, + FW_HEAD_LENGTH, &update_msg.file->f_pos); + if (ret < 0) { + GTP_ERROR("Read firmware head in update file error."); + goto load_failed; + } + memcpy(fw_head, buf, FW_HEAD_LENGTH); + + /* check firmware legality */ + fw_checksum = 0; + for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH + + FW_DSP_LENGTH + FW_BOOT_LENGTH; i + = 2) { + u16 temp; + + ret = update_msg.file->f_op->read(update_msg.file, (char *)buf, + 2, &update_msg.file->f_pos); + if (ret < 0) { + GTP_ERROR("Read firmware file error."); + goto load_failed; + } + /* GTP_DEBUG("BUF[0]:%x", buf[0]); */ + temp = (buf[0]<<8) + buf[1]; + fw_checksum += temp; + } + + GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF); + if (fw_checksum & 0xFFFF) { + GTP_ERROR("Illegal firmware file."); + goto load_failed; + } + + return SUCCESS; load_failed: - set_fs(update_msg.old_fs); - return FAIL; + set_fs(update_msg.old_fs); + return FAIL; } #if 0 -static u8 gup_check_update_header(struct i2c_client *client, st_fw_head* fw_head) +static u8 gup_check_update_header(struct i2c_client *client, + st_fw_head *fw_head) { - const u8* pos; - int i = 0; - u8 mask_num = 0; - s32 ret = 0; - - pos = HEADER_UPDATE_DATA; - - memcpy(fw_head, pos, FW_HEAD_LENGTH); - pos += FW_HEAD_LENGTH; - - ret = gup_enter_update_judge(fw_head); - if(SUCCESS == ret) - { - return SUCCESS; - } - return FAIL; + const u8 *pos; + int i = 0; + u8 mask_num = 0; + s32 ret = 0; + + pos = HEADER_UPDATE_DATA; + + memcpy(fw_head, pos, FW_HEAD_LENGTH); + pos += FW_HEAD_LENGTH; + + ret = gup_enter_update_judge(fw_head); + if (ret == SUCCESS) + return SUCCESS; + return FAIL; } #endif -static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, u16 total_length) +static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, + u16 total_length) { - s32 ret = 0; - u16 burn_addr = start_addr; - u16 frame_length = 0; - u16 burn_length = 0; - u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - u8 retry = 0; - - GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024), start_addr); - while(burn_length < total_length) - { - GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length); - frame_length = ((total_length - burn_length) > PACK_SIZE) ? PACK_SIZE : (total_length - burn_length); - wr_buf[0] = (u8)(burn_addr>>8); - rd_buf[0] = wr_buf[0]; - wr_buf[1] = (u8)burn_addr; - rd_buf[1] = wr_buf[1]; - memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], frame_length); - - for(retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) - { - ret = gup_i2c_write(client, wr_buf, GTP_ADDR_LENGTH + frame_length); - if(ret <= 0) - { - GTP_ERROR("Write frame data i2c error."); - continue; - } - ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + frame_length); - if(ret <= 0) - { - GTP_ERROR("Read back frame data i2c error."); - continue; - } - - if(memcmp(&wr_buf[GTP_ADDR_LENGTH], &rd_buf[GTP_ADDR_LENGTH], frame_length)) - { - GTP_ERROR("Check frame data fail,not equal."); - GTP_DEBUG("write array:"); - GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], frame_length); - GTP_DEBUG("read array:"); - GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); - continue; - } - else - { - //GTP_DEBUG("Check frame data success."); - break; - } - } - if(retry >= MAX_FRAME_CHECK_TIME) - { - GTP_ERROR("Burn frame data time out,exit."); - return FAIL; - } - burn_length += frame_length; - burn_addr += frame_length; - } - return SUCCESS; + s32 ret = 0; + u16 burn_addr = start_addr; + u16 frame_length = 0; + u16 burn_length = 0; + u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 retry = 0; + + GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024), + start_addr); + while (burn_length < total_length) { + GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length); + frame_length = ((total_length - burn_length) > PACK_SIZE) + ? PACK_SIZE : (total_length - burn_length); + wr_buf[0] = (u8)(burn_addr>>8); + rd_buf[0] = wr_buf[0]; + wr_buf[1] = (u8)burn_addr; + rd_buf[1] = wr_buf[1]; + memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], + frame_length); + + for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) { + ret = gup_i2c_write(client, wr_buf, + GTP_ADDR_LENGTH + frame_length); + if (ret <= 0) { + GTP_ERROR("Write frame data i2c error."); + continue; + } + ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + + frame_length); + if (ret <= 0) { + GTP_ERROR("Read back frame data i2c error."); + continue; + } + + if (memcmp(&wr_buf[GTP_ADDR_LENGTH], + &rd_buf[GTP_ADDR_LENGTH], frame_length)) { + GTP_ERROR("Check frame data fail,not equal."); + GTP_DEBUG("write array:"); + GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], + frame_length); + GTP_DEBUG("read array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], + frame_length); + continue; + } else { + /* GTP_DEBUG("Check frame data success."); */ + break; + } + } + if (retry >= MAX_FRAME_CHECK_TIME) { + GTP_ERROR("Burn frame data time out,exit."); + return FAIL; + } + burn_length += frame_length; + burn_addr += frame_length; + } + return SUCCESS; } -static u8 gup_load_section_file(u8* buf, u16 offset, u16 length) +static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length) { - s32 ret = 0; - - if(update_msg.file == NULL) - { - GTP_ERROR("cannot find update file,load section file fail."); - return FAIL; - } - update_msg.file->f_pos = FW_HEAD_LENGTH + offset; - - ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, length, &update_msg.file->f_pos); - if(ret < 0) - { - GTP_ERROR("Read update file fail."); - return FAIL; - } - - return SUCCESS; + s32 ret = 0; + + if (update_msg.file == NULL) { + GTP_ERROR("cannot find update file,load section file fail."); + return FAIL; + } + update_msg.file->f_pos = FW_HEAD_LENGTH + offset; + + ret = update_msg.file->f_op->read(update_msg.file, (char *)buf, length, + &update_msg.file->f_pos); + if (ret < 0) { + GTP_ERROR("Read update file fail."); + return FAIL; + } + + return SUCCESS; } -static u8 gup_recall_check(struct i2c_client *client, u8* chk_src, u16 start_rd_addr, u16 chk_length) +static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src, + u16 start_rd_addr, u16 chk_length) { - u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; - s32 ret = 0; - u16 recall_addr = start_rd_addr; - u16 recall_length = 0; - u16 frame_length = 0; - - while(recall_length < chk_length) - { - frame_length = ((chk_length - recall_length) > PACK_SIZE) ? PACK_SIZE : (chk_length - recall_length); - ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length); - if(ret <= 0) - { - GTP_ERROR("recall i2c error,exit"); - return FAIL; - } - - if(memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], frame_length)) - { - GTP_ERROR("Recall frame data fail,not equal."); - GTP_DEBUG("chk_src array:"); - GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length); - GTP_DEBUG("recall array:"); - GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); - return FAIL; - } - - recall_length += frame_length; - recall_addr += frame_length; - } - GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024)); - - return SUCCESS; + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + s32 ret = 0; + u16 recall_addr = start_rd_addr; + u16 recall_length = 0; + u16 frame_length = 0; + + while (recall_length < chk_length) { + frame_length = ((chk_length - recall_length) > PACK_SIZE) + ? PACK_SIZE : (chk_length - recall_length); + ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length); + if (ret <= 0) { + GTP_ERROR("recall i2c error,exit"); + return FAIL; + } + + if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], + frame_length)) { + GTP_ERROR("Recall frame data fail,not equal."); + GTP_DEBUG("chk_src array:"); + GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length); + GTP_DEBUG("recall array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); + return FAIL; + } + + recall_length += frame_length; + recall_addr += frame_length; + } + GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024)); + + return SUCCESS; } -static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, u16 start_addr, u8 bank_cmd ) +static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, + u16 start_addr, u8 bank_cmdi) { - s32 ret = 0; - u8 rd_buf[5]; - - //step1:hold ss51 & dsp - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail."); - return FAIL; - } - - //step2:set scramble - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]set scramble fail."); - return FAIL; - } - - //step3:select bank - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F); - return FAIL; - } - - //step4:enable accessing code - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]enable accessing code fail."); - return FAIL; - } - - //step5:burn 8k fw section - ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_section]burn fw_section fail."); - return FAIL; - } - - //step6:hold ss51 & release dsp - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail."); - return FAIL; - } - //must delay - msleep(1); - - //step7:send burn cmd to move data to flash from sram - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]send burn cmd fail."); - return FAIL; - } - GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......"); - do{ - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]Get burn state fail"); - return FAIL; - } - msleep(10); - //GTP_DEBUG("[burn_fw_section]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); - }while(rd_buf[GTP_ADDR_LENGTH]); - - //step8:select bank - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F); - return FAIL; - } - - //step9:enable accessing code - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]enable accessing code fail."); - return FAIL; - } - - //step10:recall 8k fw section - ret = gup_recall_check(client, fw_section, start_addr, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_section]recall check 8k firmware fail."); - return FAIL; - } - - //step11:disable accessing code - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_section]disable accessing code fail."); - return FAIL; - } - - return SUCCESS; + s32 ret = 0; + u8 rd_buf[5]; + + /* step1:hold ss51 & dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail."); + return FAIL; + } + + /* step2:set scramble */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]set scramble fail."); + return FAIL; + } + + /* step3:select bank */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, + (bank_cmd >> 4)&0x0F); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]select bank %d fail.", + (bank_cmd >> 4)&0x0F); + return FAIL; + } + + /* step4:enable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + + /* step5:burn 8k fw section */ + ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_section]burn fw_section fail."); + return FAIL; + } + + /* step6:hold ss51 & release dsp */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail."); + return FAIL; + } + /* must delay */ + msleep(20); + + /* step7:send burn cmd to move data to flash from sram */ + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]send burn cmd fail."); + return FAIL; + } + GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......"); + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]Get burn state fail"); + return FAIL; + } + msleep(20); + /* GTP_DEBUG("[burn_fw_section]Get burn state:%d.", + * rd_buf[GTP_ADDR_LENGTH]); + */ + } while (rd_buf[GTP_ADDR_LENGTH]); + + /* step8:select bank */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, + (bank_cmd >> 4)&0x0F); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]select bank %d fail.", + (bank_cmd >> 4)&0x0F); + return FAIL; + } + + /* step9:enable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + + /* step10:recall 8k fw section */ + ret = gup_recall_check(client, fw_section, start_addr, + FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_section]recall check 8k firmware fail."); + return FAIL; + } + + /* step11:disable accessing code */ + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_fw_section]disable accessing code fail."); + return FAIL; + } + + return SUCCESS; } static u8 gup_burn_dsp_isp(struct i2c_client *client) { - s32 ret = 0; - u8* fw_dsp_isp = NULL; - u8 retry = 0; - - GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>"); - - //step1:alloc memory - GTP_DEBUG("[burn_dsp_isp]step1:alloc memory"); - while(retry++ < 5) - { - fw_dsp_isp = (u8*)kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL); - if(fw_dsp_isp == NULL) - { - continue; - } - else - { - GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", (FW_DSP_ISP_LENGTH/1024)); - break; - } - } - if(retry >= 5) - { - GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit."); - return FAIL; - } - - //step2:load dsp isp file data - GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data"); - ret = gup_load_section_file(fw_dsp_isp, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail."); - goto exit_burn_dsp_isp; - } - - //step3:disable wdt,clear cache enable - GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]disable wdt fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]clear cache enable fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step4:hold ss51 & dsp - GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step5:set boot from sram - GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]set boot from sram fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step6:software reboot - GTP_DEBUG("[burn_dsp_isp]step6:software reboot"); - ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]software reboot fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step7:select bank2 - GTP_DEBUG("[burn_dsp_isp]step7:select bank2"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]select bank2 fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step8:enable accessing code - GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]enable accessing code fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - - //step9:burn 4k dsp_isp - GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp"); - ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail."); - goto exit_burn_dsp_isp; - } - - //step10:set scramble - GTP_DEBUG("[burn_dsp_isp]step10:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_dsp_isp]set scramble fail."); - ret = FAIL; - goto exit_burn_dsp_isp; - } - ret = SUCCESS; + s32 ret = 0; + u8 *fw_dsp_isp = NULL; + u8 retry = 0; + + GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>"); + + /* step1:alloc memory */ + GTP_DEBUG("[burn_dsp_isp]step1:alloc memory"); + while (retry++ < 5) { + fw_dsp_isp = kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL); + if (fw_dsp_isp == NULL) { + continue; + } else { + GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", + (FW_DSP_ISP_LENGTH/1024)); + break; + } + } + if (retry >= 5) { + GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit."); + return FAIL; + } + + /* step2:load dsp isp file data */ + GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data"); + ret = gup_load_section_file(fw_dsp_isp, (4 * FW_SECTION_LENGTH + + FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail."); + goto exit_burn_dsp_isp; + } + + /* step3:disable wdt,clear cache enable */ + GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]disable wdt fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]clear cache enable fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step4:hold ss51 & dsp */ + GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step5:set boot from sram */ + GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]set boot from sram fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step6:software reboot */ + GTP_DEBUG("[burn_dsp_isp]step6:software reboot"); + ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]software reboot fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step7:select bank2 */ + GTP_DEBUG("[burn_dsp_isp]step7:select bank2"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]select bank2 fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step8:enable accessing code */ + GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]enable accessing code fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + /* step9:burn 4k dsp_isp */ + GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp"); + ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail."); + goto exit_burn_dsp_isp; + } + + /* step10:set scramble */ + GTP_DEBUG("[burn_dsp_isp]step10:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_dsp_isp]set scramble fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + ret = SUCCESS; exit_burn_dsp_isp: - kfree(fw_dsp_isp); - return ret; + kfree(fw_dsp_isp); + return ret; } static u8 gup_burn_fw_ss51(struct i2c_client *client) { - u8* fw_ss51 = NULL; - u8 retry = 0; - s32 ret = 0; - - GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>"); - - //step1:alloc memory - GTP_DEBUG("[burn_fw_ss51]step1:alloc memory"); - while(retry++ < 5) - { - fw_ss51 = (u8*)kzalloc(FW_SECTION_LENGTH, GFP_KERNEL); - if(fw_ss51 == NULL) - { - continue; - } - else - { - GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024)); - break; - } - } - if(retry >= 5) - { - GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit."); - return FAIL; - } - - //step2:load ss51 firmware section 1 file data - GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data"); - ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail."); - goto exit_burn_fw_ss51; - } - - //step3:clear control flag - GTP_DEBUG("[burn_fw_ss51]step3:clear control flag"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_ss51]clear control flag fail."); - ret = FAIL; - goto exit_burn_fw_ss51; - } - - //step4:burn ss51 firmware section 1 - GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1"); - ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail."); - goto exit_burn_fw_ss51; - } - - //step5:load ss51 firmware section 2 file data - GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data"); - ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail."); - goto exit_burn_fw_ss51; - } - - //step6:burn ss51 firmware section 2 - GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2"); - ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail."); - goto exit_burn_fw_ss51; - } - - //step7:load ss51 firmware section 3 file data - GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data"); - ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail."); - goto exit_burn_fw_ss51; - } - - //step8:burn ss51 firmware section 3 - GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3"); - ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail."); - goto exit_burn_fw_ss51; - } - - //step9:load ss51 firmware section 4 file data - GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data"); - ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, FW_SECTION_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail."); - goto exit_burn_fw_ss51; - } - - //step10:burn ss51 firmware section 4 - GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4"); - ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail."); - goto exit_burn_fw_ss51; - } - - ret = SUCCESS; - + u8 *fw_ss51 = NULL; + u8 retry = 0; + s32 ret = 0; + + GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>"); + + /* step1:alloc memory */ + GTP_DEBUG("[burn_fw_ss51]step1:alloc memory"); + while (retry++ < 5) { + fw_ss51 = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL); + if (fw_ss51 == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", + (FW_SECTION_LENGTH/1024)); + break; + } + } + if (retry >= 5) { + GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit."); + return FAIL; + } + + /* step2:load ss51 firmware section 1 file data */ + GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data"); + ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + + /* step3:clear control flag */ + GTP_DEBUG("[burn_fw_ss51]step3:clear control flag"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_fw_ss51]clear control flag fail."); + ret = FAIL; + goto exit_burn_fw_ss51; + } + + /* step4:burn ss51 firmware section 1 */ + GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + + /* step5:load ss51 firmware section 2 file data */ + GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data"); + ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, + FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + + /* step6:burn ss51 firmware section 2 */ + GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + + /* step7:load ss51 firmware section 3 file data */ + GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data"); + ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, + FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + + /* step8:burn ss51 firmware section 3 */ + GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + + /* step9:load ss51 firmware section 4 file data */ + GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data"); + ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, + FW_SECTION_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + + /* step10:burn ss51 firmware section 4 */ + GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + + ret = SUCCESS; + exit_burn_fw_ss51: - kfree(fw_ss51); - return ret; + kfree(fw_ss51); + return ret; } static u8 gup_burn_fw_dsp(struct i2c_client *client) { - s32 ret = 0; - u8* fw_dsp = NULL; - u8 retry = 0; - u8 rd_buf[5]; - - GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>"); - //step1:alloc memory - GTP_DEBUG("[burn_fw_dsp]step1:alloc memory"); - while(retry++ < 5) - { - fw_dsp = (u8*)kzalloc(FW_DSP_LENGTH, GFP_KERNEL); - if(fw_dsp == NULL) - { - continue; - } - else - { - GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024)); - break; - } - } - if(retry >= 5) - { - GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit."); - return FAIL; - } - - //step2:load firmware dsp - GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp"); - ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_dsp]load firmware dsp fail."); - goto exit_burn_fw_dsp; - } - - //step3:select bank3 - GTP_DEBUG("[burn_fw_dsp]step3:select bank3"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]select bank3 fail."); - ret = FAIL; - goto exit_burn_fw_dsp; - } - - //step4:hold ss51 & dsp - GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_dsp; - } - - //step5:set scramble - GTP_DEBUG("[burn_fw_dsp]step5:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]set scramble fail."); - ret = FAIL; - goto exit_burn_fw_dsp; - } - - //step6:release ss51 & dsp - GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211 - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_dsp; - } - //must delay - msleep(1); - - //step7:burn 4k dsp firmware - GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware"); - ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_dsp]burn fw_section fail."); - goto exit_burn_fw_dsp; - } - - //step8:send burn cmd to move data to flash from sram - GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]send burn cmd fail."); - goto exit_burn_fw_dsp; - } - GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......"); - do{ - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_dsp]Get burn state fail"); - goto exit_burn_fw_dsp; - } - msleep(10); - //GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); - }while(rd_buf[GTP_ADDR_LENGTH]); - - //step9:recall check 4k dsp firmware - GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware"); - ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail."); - goto exit_burn_fw_dsp; - } - - ret = SUCCESS; - + s32 ret = 0; + u8 *fw_dsp = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>"); + /* step1:alloc memory */ + GTP_DEBUG("[burn_fw_dsp]step1:alloc memory"); + while (retry++ < 5) { + fw_dsp = kzalloc(FW_DSP_LENGTH, GFP_KERNEL); + if (fw_dsp == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", + (FW_SECTION_LENGTH/1024)); + break; + } + } + if (retry >= 5) { + GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit."); + return FAIL; + } + + /* step2:load firmware dsp */ + GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp"); + ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_dsp]load firmware dsp fail."); + goto exit_burn_fw_dsp; + } + + /* step3:select bank3 */ + GTP_DEBUG("[burn_fw_dsp]step3:select bank3"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]select bank3 fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + /* Step4:hold ss51 & dsp */ + GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + /* step5:set scramble */ + GTP_DEBUG("[burn_fw_dsp]step5:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + /* step6:release ss51 & dsp */ + GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + /* must delay */ + msleep(20); + + /* step7:burn 4k dsp firmware */ + GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware"); + ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_dsp]burn fw_section fail."); + goto exit_burn_fw_dsp; + } + + /* step8:send burn cmd to move data to flash from sram */ + GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash", + "from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]send burn cmd fail."); + goto exit_burn_fw_dsp; + } + GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......"); + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if (ret <= 0) { + GTP_ERROR("[burn_fw_dsp]Get burn state fail"); + goto exit_burn_fw_dsp; + } + msleep(20); + /* GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", + * rd_buf[GTP_ADDR_LENGTH]); + */ + } while (rd_buf[GTP_ADDR_LENGTH]); + + /* step9:recall check 4k dsp firmware */ + GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware"); + ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail."); + goto exit_burn_fw_dsp; + } + + ret = SUCCESS; + exit_burn_fw_dsp: - kfree(fw_dsp); - return ret; + kfree(fw_dsp); + return ret; } static u8 gup_burn_fw_boot(struct i2c_client *client) { - s32 ret = 0; - u8* fw_boot = NULL; - u8 retry = 0; - u8 rd_buf[5]; - - GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>"); - - //step1:Alloc memory - GTP_DEBUG("[burn_fw_boot]step1:Alloc memory"); - while(retry++ < 5) - { - fw_boot = (u8*)kzalloc(FW_BOOT_LENGTH, GFP_KERNEL); - if(fw_boot == NULL) - { - continue; - } - else - { - GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", (FW_BOOT_LENGTH/1024)); - break; - } - } - if(retry >= 5) - { - GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit."); - return FAIL; - } - - //step2:load firmware bootloader - GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader"); - ret = gup_load_section_file(fw_boot, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH), FW_BOOT_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_boot]load firmware dsp fail."); - goto exit_burn_fw_boot; - } - - //step3:hold ss51 & dsp - GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - - //step4:set scramble - GTP_DEBUG("[burn_fw_boot]step4:set scramble"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]set scramble fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - - //step5:release ss51 & dsp - GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211 - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - //must delay - msleep(1); - - //step6:select bank3 - GTP_DEBUG("[burn_fw_boot]step6:select bank3"); - ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]select bank3 fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - - //step7:burn 2k bootloader firmware - GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware"); - ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_boot]burn fw_section fail."); - goto exit_burn_fw_boot; - } - - //step7:send burn cmd to move data to flash from sram - GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to flash from sram"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]send burn cmd fail."); - goto exit_burn_fw_boot; - } - GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......"); - do{ - ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]Get burn state fail"); - goto exit_burn_fw_boot; - } - msleep(10); - //GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); - }while(rd_buf[GTP_ADDR_LENGTH]); - - //step8:recall check 2k bootloader firmware - GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware"); - ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); - if(FAIL == ret) - { - GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail."); - goto exit_burn_fw_boot; - } - - //step9:enable download DSP code - GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code "); - ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]enable download DSP code fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - - //step10:release ss51 & hold dsp - GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp"); - ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); - if(ret <= 0) - { - GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; - } - - ret = SUCCESS; - + s32 ret = 0; + u8 *fw_boot = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>"); + + /* step1:Alloc memory */ + GTP_DEBUG("[burn_fw_boot]step1:Alloc memory"); + while (retry++ < 5) { + fw_boot = kzalloc(FW_BOOT_LENGTH, GFP_KERNEL); + if (fw_boot == NULL) { + continue; + } else { + GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", + (FW_BOOT_LENGTH/1024)); + break; + } + } + if (retry >= 5) { + GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit."); + return FAIL; + } + + /* step2:load firmware bootloader */ + GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader"); + ret = gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH + + FW_DSP_LENGTH), FW_BOOT_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_boot]load firmware dsp fail."); + goto exit_burn_fw_boot; + } + + /* step3:hold ss51 & dsp */ + GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + /* step4:set scramble */ + GTP_DEBUG("[burn_fw_boot]step4:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + /* step5:release ss51 & dsp */ + GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + /* must delay */ + msleep(20); + + /* step6:select bank3 */ + GTP_DEBUG("[burn_fw_boot]step6:select bank3"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]select bank3 fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + /* step7:burn 2k bootloader firmware */ + GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware"); + ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_boot]burn fw_section fail."); + goto exit_burn_fw_boot; + } + + /* step7:send burn cmd to move data to flash from sram */ + GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to", + "flash from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]send burn cmd fail."); + goto exit_burn_fw_boot; + } + GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......"); + do { + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]Get burn state fail"); + goto exit_burn_fw_boot; + } + msleep(20); + /* GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", + * rd_buf[GTP_ADDR_LENGTH]); + */ + } while (rd_buf[GTP_ADDR_LENGTH]); + + /* step8:recall check 2k bootloader firmware */ + GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware"); + ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + if (ret == FAIL) { + GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail."); + goto exit_burn_fw_boot; + } + + /* step9:enable download DSP code */ + GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code "); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]enable download DSP code fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + /* step10:release ss51 & hold dsp */ + GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); + if (ret <= 0) { + GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + ret = SUCCESS; + exit_burn_fw_boot: - kfree(fw_boot); - return ret; + kfree(fw_boot); + return ret; } s32 gup_update_proc(void *dir) { - s32 ret = 0; - u8 retry = 0; - st_fw_head fw_head; - struct goodix_ts_data *ts = NULL; - - GTP_DEBUG("[update_proc]Begin update ......"); - - show_len = 1; - total_len = 100; - if(dir == NULL) - { - msleep(3000); //wait main thread to be completed - } - - ts = i2c_get_clientdata(i2c_connect_client); - - if (searching_file) - { - searching_file = 0; // exit .bin update file searching - GTP_INFO("Exiting searching .bin update file..."); - while ((show_len != 200) && (show_len != 100)) // wait for auto update quitted completely - { - msleep(100); - } - } - - update_msg.file = NULL; - ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8*)dir); //20121211 - if(FAIL == ret) - { - GTP_ERROR("[update_proc]check update file fail."); - goto file_fail; - } - - //gtp_reset_guitar(i2c_connect_client, 20); - ret = gup_get_ic_fw_msg(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]get ic message fail."); - goto file_fail; - } - - ret = gup_enter_update_judge(&fw_head); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]Check *.bin file fail."); - goto file_fail; - } - - ts->enter_update = 1; - gtp_irq_disable(ts); + s32 ret = 0; + u8 retry = 0; + st_fw_head fw_head; + struct goodix_ts_data *ts = NULL; + + GTP_DEBUG("[update_proc]Begin update ......"); + + show_len = 1; + total_len = 100; + if (dir == NULL) + /* wait main thread to be completed */ + msleep(3000); + + ts = i2c_get_clientdata(i2c_connect_client); + + if (searching_file) { + /* exit .bin update file searching */ + searching_file = 0; + GTP_INFO("Exiting searching .bin update file..."); + /* wait for auto update quitted completely */ + while ((show_len != 200) && (show_len != 100)) + msleep(100); + } + + update_msg.file = NULL; + ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8 *)dir); + if (ret == FAIL) { + GTP_ERROR("[update_proc]check update file fail."); + goto file_fail; + } + + /* gtp_reset_guitar(i2c_connect_client, 20); */ + ret = gup_get_ic_fw_msg(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]get ic message fail."); + goto file_fail; + } + + ret = gup_enter_update_judge(&fw_head); + if (ret == FAIL) { + GTP_ERROR("[update_proc]Check *.bin file fail."); + goto file_fail; + } + + ts->enter_update = 1; + gtp_irq_disable(ts); #if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_OFF); + gtp_esd_switch(ts->client, SWITCH_OFF); #endif - ret = gup_enter_update_mode(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]enter update mode fail."); - goto update_fail; - } - - while(retry++ < 5) - { - show_len = 10; - total_len = 100; - ret = gup_burn_dsp_isp(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]burn dsp isp fail."); - continue; - } - - show_len += 10; - ret = gup_burn_fw_ss51(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]burn ss51 firmware fail."); - continue; - } - - show_len += 40; - ret = gup_burn_fw_dsp(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]burn dsp firmware fail."); - continue; - } - - show_len += 20; - ret = gup_burn_fw_boot(i2c_connect_client); - if(FAIL == ret) - { - GTP_ERROR("[update_proc]burn bootloader firmware fail."); - continue; - } - show_len += 10; - GTP_INFO("[update_proc]UPDATE SUCCESS."); - break; - } - if(retry >= 5) - { - GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL."); - goto update_fail; - } - - GTP_DEBUG("[update_proc]leave update mode."); - gup_leave_update_mode(); - - msleep(100); -// GTP_DEBUG("[update_proc]send config."); -// ret = gtp_send_cfg(i2c_connect_client); -// if(ret < 0) -// { -// GTP_ERROR("[update_proc]send config fail."); -// } - if (ts->fw_error) - { - GTP_INFO("firmware error auto update, resent config!"); - gup_init_panel(ts); - } - show_len = 100; - total_len = 100; - ts->enter_update = 0; - gtp_irq_enable(ts); - + ret = gup_enter_update_mode(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]enter update mode fail."); + goto update_fail; + } + + while (retry++ < 5) { + show_len = 10; + total_len = 100; + ret = gup_burn_dsp_isp(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]burn dsp isp fail."); + continue; + } + + show_len += 10; + ret = gup_burn_fw_ss51(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]burn ss51 firmware fail."); + continue; + } + + show_len += 40; + ret = gup_burn_fw_dsp(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]burn dsp firmware fail."); + continue; + } + + show_len += 20; + ret = gup_burn_fw_boot(i2c_connect_client); + if (ret == FAIL) { + GTP_ERROR("[update_proc]burn bootloader fw fail."); + continue; + } + show_len += 10; + GTP_INFO("[update_proc]UPDATE SUCCESS."); + break; + } + if (retry >= 5) { + GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL."); + goto update_fail; + } + + GTP_DEBUG("[update_proc]leave update mode."); + gup_leave_update_mode(); + + msleep(100); + + /* GTP_DEBUG("[update_proc]send config."); + * ret = gtp_send_cfg(i2c_connect_client); + * if(ret < 0) { + * GTP_ERROR("[update_proc]send config fail."); + * } + */ + + if (ts->fw_error) { + GTP_INFO("firmware error auto update, resent config!"); + gup_init_panel(ts); + } + show_len = 100; + total_len = 100; + ts->enter_update = 0; + gtp_irq_enable(ts); + #if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_ON); + gtp_esd_switch(ts->client, SWITCH_ON); #endif - filp_close(update_msg.file, NULL); - return SUCCESS; - + filp_close(update_msg.file, NULL); + return SUCCESS; + update_fail: - ts->enter_update = 0; - gtp_irq_enable(ts); - + ts->enter_update = 0; + gtp_irq_enable(ts); + #if GTP_ESD_PROTECT - gtp_esd_switch(ts->client, SWITCH_ON); + gtp_esd_switch(ts->client, SWITCH_ON); #endif file_fail: - if(update_msg.file && !IS_ERR(update_msg.file)) - { - filp_close(update_msg.file, NULL); - } - show_len = 200; - total_len = 100; - return FAIL; + if (update_msg.file && !IS_ERR(update_msg.file)) + filp_close(update_msg.file, NULL); + + show_len = 200; + total_len = 100; + return FAIL; } #if GTP_AUTO_UPDATE u8 gup_init_update_proc(struct goodix_ts_data *ts) { - struct task_struct *thread = NULL; + struct task_struct *thread = NULL; - GTP_INFO("Ready to run update thread."); - thread = kthread_run(gup_update_proc, (void*)NULL, "guitar_update"); - if (IS_ERR(thread)) - { - GTP_ERROR("Failed to create update thread.\n"); - return -1; - } + GTP_INFO("Ready to run update thread."); + thread = kthread_run(gup_update_proc, (void *)NULL, "guitar_update"); + if (IS_ERR(thread)) { + GTP_ERROR("Failed to create update thread.\n"); + return -EINVAL; + } - return 0; + return 0; } -#endif
\ No newline at end of file +#endif diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 9080a13069a8..88b1d3c013c7 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -117,6 +117,7 @@ #define PD_FLAGS_DATA_TYPE_BITS 0xF0 /* other types (like chip-detected gestures) exist but we do not care */ #define PD_FLAGS_DATA_TYPE_TOUCH 0x00 +#define PD_FLAGS_IDLE_TO_ACTIVE 0x10 /* a bit for each finger data that is valid (from lsb to msb) */ #define PD_FLAGS_HAVE_FINGERS 0x07 #define PD_PALM_FLAG_BIT 0x01 @@ -137,15 +138,15 @@ #define PINCTRL_STATE_RELEASE "pmx_ts_release" struct finger_data { - uint8_t xLo; - uint8_t hi; - uint8_t yLo; - uint8_t pressure; + u8 xLo; + u8 hi; + u8 yLo; + u8 pressure; } __packed; struct point_data { - uint8_t flags; - uint8_t palm; + u8 flags; + u8 gesture_id; struct finger_data fd[3]; } __packed; @@ -179,7 +180,7 @@ struct it7260_ts_data { const struct it7260_ts_platform_data *pdata; struct regulator *vdd; struct regulator *avdd; - bool device_needs_wakeup; + bool in_low_power_mode; bool suspended; bool fw_upgrade_result; bool cfg_upgrade_result; @@ -743,18 +744,31 @@ out: static int it7260_ts_chip_low_power_mode(struct it7260_ts_data *ts_data, const u8 sleep_type) { - const uint8_t cmd_sleep[] = {CMD_PWR_CTL, 0x00, sleep_type}; - uint8_t dummy; + const u8 cmd_sleep[] = {CMD_PWR_CTL, 0x00, sleep_type}; + u8 dummy; + int ret; - if (sleep_type) - it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, cmd_sleep, - sizeof(cmd_sleep)); - else - it7260_i2c_read_no_ready_check(ts_data, BUF_QUERY, &dummy, + if (sleep_type) { + ret = it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, + cmd_sleep, sizeof(cmd_sleep)); + if (ret != IT_I2C_WRITE_RET) + dev_err(&ts_data->client->dev, + "Can't go to sleep or low power mode(%d) %d\n", + sleep_type, ret); + else + ret = 0; + } else { + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_QUERY, &dummy, sizeof(dummy)); + if (ret != IT_I2C_READ_RET) + dev_err(&ts_data->client->dev, + "Can't go to active mode %d\n", ret); + else + ret = 0; + } msleep(WAIT_CHANGE_MODE); - return 0; + return ret; } static ssize_t sysfs_fw_upgrade_store(struct device *dev, @@ -1226,18 +1240,28 @@ static irqreturn_t it7260_ts_threaded_handler(int irq, void *devid) * schedule a work that tells the pm core to relax once the CPU * cores are up. */ - if (ts_data->device_needs_wakeup) { + if ((pt_data.flags & PD_FLAGS_DATA_TYPE_BITS) == + PD_FLAGS_IDLE_TO_ACTIVE && + pt_data.gesture_id == 0) { pm_stay_awake(&ts_data->client->dev); input_report_key(input_dev, KEY_WAKEUP, 1); input_sync(input_dev); input_report_key(input_dev, KEY_WAKEUP, 0); input_sync(input_dev); schedule_work(&ts_data->work_pm_relax); - return IRQ_HANDLED; + } else { + dev_dbg(&ts_data->client->dev, + "Ignore the touch data\n"); } + return IRQ_HANDLED; } - palm_detected = pt_data.palm & PD_PALM_FLAG_BIT; + /* + * Check if touch data also includes any palm gesture or not. + * If palm gesture is detected, then send the keycode parsed + * from the DT. + */ + palm_detected = pt_data.gesture_id & PD_PALM_FLAG_BIT; if (palm_detected && ts_data->pdata->palm_detect_en) { input_report_key(input_dev, ts_data->pdata->palm_detect_keycode, 1); @@ -2084,7 +2108,7 @@ static int it7260_ts_resume(struct device *dev) int retval; if (device_may_wakeup(dev)) { - if (ts_data->device_needs_wakeup) { + if (ts_data->in_low_power_mode) { /* Set active current for the avdd regulator */ if (ts_data->pdata->avdd_lpm_cur) { retval = reg_set_optimum_mode_check( @@ -2095,7 +2119,7 @@ static int it7260_ts_resume(struct device *dev) retval); } - ts_data->device_needs_wakeup = false; + ts_data->in_low_power_mode = false; disable_irq_wake(ts_data->client->irq); } return 0; @@ -2130,10 +2154,13 @@ static int it7260_ts_suspend(struct device *dev) } if (device_may_wakeup(dev)) { - if (!ts_data->device_needs_wakeup) { + if (!ts_data->in_low_power_mode) { /* put the device in low power idle mode */ - it7260_ts_chip_low_power_mode(ts_data, + retval = it7260_ts_chip_low_power_mode(ts_data, PWR_CTL_LOW_POWER_MODE); + if (retval) + dev_err(dev, "Can't go to low power mode %d\n", + retval); /* Set lpm current for avdd regulator */ if (ts_data->pdata->avdd_lpm_cur) { @@ -2145,7 +2172,7 @@ static int it7260_ts_suspend(struct device *dev) retval); } - ts_data->device_needs_wakeup = true; + ts_data->in_low_power_mode = true; enable_irq_wake(ts_data->client->irq); } return 0; diff --git a/drivers/input/touchscreen/msg21xx_ts.c b/drivers/input/touchscreen/msg21xx_ts.c index 8f2c4da578fc..fe8c6e1647f8 100644 --- a/drivers/input/touchscreen/msg21xx_ts.c +++ b/drivers/input/touchscreen/msg21xx_ts.c @@ -30,10 +30,8 @@ #include <linux/init.h> #include <linux/mutex.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/firmware.h> -#include <linux/string.h> -#include <linux/kthread.h> +#include <linux/debugfs.h> #include <linux/regulator/consumer.h> #if defined(CONFIG_FB) @@ -67,6 +65,13 @@ #define MSTAR_CHIPTOP_REGISTER_BANK 0x1E #define MSTAR_CHIPTOP_REGISTER_ICTYPE 0xCC #define MSTAR_INIT_SW_ID 0x7FF +#define MSTAR_DEBUG_DIR_NAME "ts_debug" + +#define MSG_FW_FILE_MAJOR_VERSION(x) \ + (((x)->data[0x7f4f] << 8) + ((x)->data[0x7f4e])) + +#define MSG_FW_FILE_MINOR_VERSION(x) \ + (((x)->data[0x7f51] << 8) + ((x)->data[0x7f50])) /* * Note. @@ -89,7 +94,6 @@ static unsigned char MSG_FIRMWARE[1][33*1024] = { { #endif #define CONFIG_TP_HAVE_KEY - #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" #define PINCTRL_STATE_RELEASE "pmx_ts_release" @@ -97,30 +101,16 @@ static unsigned char MSG_FIRMWARE[1][33*1024] = { { #define SLAVE_I2C_ID_DBBUS (0xC4>>1) #define DEMO_MODE_PACKET_LENGTH (8) -#define MAX_TOUCH_NUM (2) #define TP_PRINT -#ifdef TP_PRINT -static int tp_print_proc_read(void); -static void tp_print_create_entry(void); -#endif static char *fw_version; /* customer firmware version */ static unsigned short fw_version_major; static unsigned short fw_version_minor; static unsigned char temp[94][1024]; static unsigned int crc32_table[256]; -static int FwDataCnt; -static unsigned char bFwUpdating; -static struct class *firmware_class; -static struct device *firmware_cmd_dev; -static struct i2c_client *i2c_client; - -static u32 button_map[MAX_BUTTONS]; -static u32 num_buttons; - -static unsigned short update_bin_major, update_bin_minor; +static unsigned short fw_file_major, fw_file_minor; static unsigned short main_sw_id = MSTAR_INIT_SW_ID; static unsigned short info_sw_id = MSTAR_INIT_SW_ID; static unsigned int bin_conf_crc32; @@ -128,15 +118,12 @@ static unsigned int bin_conf_crc32; struct msg21xx_ts_platform_data { const char *name; char fw_name[MSTAR_FW_NAME_MAX_LEN]; - char *fw_version; - unsigned short fw_version_major; - unsigned short fw_version_minor; - u32 irqflags; + u8 fw_version_major; + u8 fw_version_minor; u32 irq_gpio; u32 irq_gpio_flags; u32 reset_gpio; u32 reset_gpio_flags; - u32 family_id; u32 x_max; u32 y_max; u32 x_min; @@ -153,26 +140,33 @@ struct msg21xx_ts_platform_data { int (*power_on)(bool); int (*power_init)(bool); int (*power_on)(bool); + u8 ic_type; + u32 button_map[MAX_BUTTONS]; + u32 num_buttons; + u32 hard_reset_delay_ms; + u32 post_hard_reset_delay_ms; + bool updating_fw; +}; + +/* Touch Data Type Definition */ +struct touchPoint_t { + unsigned short x; + unsigned short y; }; -static struct msg21xx_ts_platform_data *pdata; +struct touchInfo_t { + struct touchPoint_t *point; + unsigned char count; + unsigned char keycode; +}; struct msg21xx_ts_data { struct i2c_client *client; struct input_dev *input_dev; - const struct msg21xx_ts_platform_data *pdata; + struct msg21xx_ts_platform_data *pdata; struct regulator *vdd; struct regulator *vcc_i2c; - bool loading_fw; - u8 family_id; - struct dentry *dir; - u16 addr; bool suspended; - char *ts_info; - u8 *tch_data; - u32 tch_data_len; - u8 fw_ver[3]; - u8 fw_vendor_id; #if defined(CONFIG_FB) struct notifier_block fb_notif; #endif @@ -181,10 +175,9 @@ struct msg21xx_ts_data { struct pinctrl_state *pinctrl_state_suspend; struct pinctrl_state *pinctrl_state_release; struct mutex ts_mutex; + struct touchInfo_t info; }; -static struct msg21xx_ts_data *ts_data; - #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); @@ -195,23 +188,15 @@ static unsigned char bEnableTpProximity; static unsigned char bFaceClosingTp; #endif -static struct mutex msg21xx_mutex; -static struct input_dev *input_dev; - - -/* Data Type Definition */ +#ifdef TP_PRINT +static int tp_print_proc_read(struct msg21xx_ts_data *ts_data); +static void tp_print_create_entry(struct msg21xx_ts_data *ts_data); +#endif -struct touchPoint_t { - unsigned short x; - unsigned short y; -}; - -struct touchInfo_t { - struct touchPoint_t point[MAX_TOUCH_NUM]; - unsigned char count; - unsigned char keycode; -}; +static void _ReadBinConfig(struct msg21xx_ts_data *ts_data); +static unsigned int _CalMainCRC32(struct msg21xx_ts_data *ts_data); +static struct mutex msg21xx_mutex; enum EMEM_TYPE_t { EMEM_ALL = 0, @@ -259,26 +244,25 @@ static void _CRC_initTable(void) } } -static void reset_hw(void) +static void msg21xx_reset_hw(struct msg21xx_ts_platform_data *pdata) { - DBG("reset_hw()\n"); - gpio_direction_output(pdata->reset_gpio, 1); gpio_set_value_cansleep(pdata->reset_gpio, 0); - msleep(100); /* Note that the RST must be in LOW 10ms at least */ + /* Note that the RST must be in LOW 10ms at least */ + usleep(pdata->hard_reset_delay_ms * 1000); gpio_set_value_cansleep(pdata->reset_gpio, 1); /* Enable the interrupt service thread/routine for INT after 50ms */ - msleep(100); + usleep(pdata->post_hard_reset_delay_ms * 1000); } -static int read_i2c_seq(unsigned char addr, unsigned char *buf, - unsigned short size) +static int read_i2c_seq(struct msg21xx_ts_data *ts_data, unsigned char addr, + unsigned char *buf, unsigned short size) { int rc = 0; struct i2c_msg msgs[] = { { .addr = addr, - .flags = I2C_M_RD, /* read flag*/ + .flags = I2C_M_RD, /* read flag */ .len = size, .buf = buf, }, @@ -287,25 +271,27 @@ static int read_i2c_seq(unsigned char addr, unsigned char *buf, /* If everything went ok (i.e. 1 msg transmitted), return #bytes * transmitted, else error code. */ - if (i2c_client != NULL) { - rc = i2c_transfer(i2c_client->adapter, msgs, 1); + if (ts_data->client != NULL) { + rc = i2c_transfer(ts_data->client->adapter, msgs, 1); if (rc < 0) - DBG("read_i2c_seq() error %d\n", rc); + dev_err(&ts_data->client->dev, + "%s error %d\n", __func__, rc); } else { - DBG("i2c_client is NULL\n"); + dev_err(&ts_data->client->dev, "ts_data->client is NULL\n"); } return rc; } -static int write_i2c_seq(unsigned char addr, unsigned char *buf, - unsigned short size) +static int write_i2c_seq(struct msg21xx_ts_data *ts_data, unsigned char addr, + unsigned char *buf, unsigned short size) { int rc = 0; struct i2c_msg msgs[] = { { .addr = addr, - /* if read flag is undefined, + /* + * if read flag is undefined, * then it means write flag. */ .flags = 0, @@ -314,48 +300,55 @@ static int write_i2c_seq(unsigned char addr, unsigned char *buf, }, }; - /* If everything went ok (i.e. 1 msg transmitted), return #bytes + /* + * If everything went ok (i.e. 1 msg transmitted), return #bytes * transmitted, else error code. */ - if (i2c_client != NULL) { - rc = i2c_transfer(i2c_client->adapter, msgs, 1); + if (ts_data->client != NULL) { + rc = i2c_transfer(ts_data->client->adapter, msgs, 1); if (rc < 0) - DBG("write_i2c_seq() error %d\n", rc); + dev_err(&ts_data->client->dev, + "%s error %d\n", __func__, rc); } else { - DBG("i2c_client is NULL\n"); + dev_err(&ts_data->client->dev, "ts_data->client is NULL\n"); } return rc; } -static unsigned short read_reg(unsigned char bank, unsigned char addr) +static unsigned short read_reg(struct msg21xx_ts_data *ts_data, + unsigned char bank, unsigned char addr) { unsigned char tx_data[3] = {0x10, bank, addr}; unsigned char rx_data[2] = {0}; - write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 3); - read_i2c_seq(SLAVE_I2C_ID_DBBUS, &rx_data[0], 2); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data)); + read_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, rx_data, sizeof(rx_data)); return rx_data[1] << 8 | rx_data[0]; } -static void write_reg(unsigned char bank, unsigned char addr, +static void write_reg(struct msg21xx_ts_data *ts_data, unsigned char bank, + unsigned char addr, unsigned short data) { unsigned char tx_data[5] = {0x10, bank, addr, data & 0xFF, data >> 8}; write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 5); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data)); } -static void write_reg_8bit(unsigned char bank, unsigned char addr, +static void write_reg_8bit(struct msg21xx_ts_data *ts_data, unsigned char bank, + unsigned char addr, unsigned char data) { unsigned char tx_data[4] = {0x10, bank, addr, data}; write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 4); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, tx_data, sizeof(tx_data)); } -static void dbbusDWIICEnterSerialDebugMode(void) +static void dbbusDWIICEnterSerialDebugMode(struct msg21xx_ts_data *ts_data) { unsigned char data[5]; @@ -366,120 +359,109 @@ static void dbbusDWIICEnterSerialDebugMode(void) data[3] = 0x44; data[4] = 0x42; - write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 5); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data)); } -static void dbbusDWIICStopMCU(void) +static void dbbusDWIICStopMCU(struct msg21xx_ts_data *ts_data) { unsigned char data[1]; /* Stop the MCU */ data[0] = 0x37; - write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data)); } -static void dbbusDWIICIICUseBus(void) +static void dbbusDWIICIICUseBus(struct msg21xx_ts_data *ts_data) { unsigned char data[1]; /* IIC Use Bus */ data[0] = 0x35; - write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data)); } -static void dbbusDWIICIICReshape(void) +static void dbbusDWIICIICReshape(struct msg21xx_ts_data *ts_data) { unsigned char data[1]; /* IIC Re-shape */ data[0] = 0x71; - write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); + write_i2c_seq(ts_data, SLAVE_I2C_ID_DBBUS, data, sizeof(data)); } -static unsigned char get_ic_type(void) +static unsigned char msg21xx_get_ic_type(struct msg21xx_ts_data *ts_data) { unsigned char ic_type = 0; unsigned char bank; unsigned char addr; - reset_hw(); - dbbusDWIICEnterSerialDebugMode(); - dbbusDWIICStopMCU(); - dbbusDWIICIICUseBus(); - dbbusDWIICIICReshape(); + msg21xx_reset_hw(ts_data->pdata); + dbbusDWIICEnterSerialDebugMode(ts_data); + dbbusDWIICStopMCU(ts_data); + dbbusDWIICIICUseBus(ts_data); + dbbusDWIICIICReshape(ts_data); msleep(300); /* stop mcu */ - write_reg_8bit(0x0F, 0xE6, 0x01); + write_reg_8bit(ts_data, 0x0F, 0xE6, 0x01); /* disable watch dog */ - write_reg(0x3C, 0x60, 0xAA55); + write_reg(ts_data, 0x3C, 0x60, 0xAA55); /* get ic type */ bank = MSTAR_CHIPTOP_REGISTER_BANK; addr = MSTAR_CHIPTOP_REGISTER_ICTYPE; - ic_type = (0xff)&(read_reg(bank, addr)); + ic_type = (0xff)&(read_reg(ts_data, bank, addr)); - if (ic_type != 1 /* msg2133 */ - && ic_type != 2 /* msg21xxA */ - && ic_type != 3) /* msg26xxM */ { + if (ic_type != ts_data->pdata->ic_type) ic_type = 0; - } - reset_hw(); + msg21xx_reset_hw(ts_data->pdata); return ic_type; } -static int msg21xx_read_firmware_id(void) +static int msg21xx_read_firmware_id(struct msg21xx_ts_data *ts_data) { - unsigned char dbbus_tx_data[3] = {0}; - unsigned char dbbus_rx_data[4] = {0}; - int ret = 0; + unsigned char command[3] = { 0x53, 0x00, 0x2A}; + unsigned char response[4] = { 0 }; - dbbus_tx_data[0] = 0x53; - dbbus_tx_data[1] = 0x00; - dbbus_tx_data[2] = 0x2A; mutex_lock(&msg21xx_mutex); - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 3); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 4); + write_i2c_seq(ts_data, ts_data->client->addr, command, sizeof(command)); + read_i2c_seq(ts_data, ts_data->client->addr, response, + sizeof(response)); mutex_unlock(&msg21xx_mutex); - pdata->fw_version_major = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0]; - pdata->fw_version_minor = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2]; - - dev_dbg(&i2c_client->dev, "major num = %d, minor num = %d\n", - pdata->fw_version_major, pdata->fw_version_minor); - - if (pdata->fw_version == NULL) - pdata->fw_version = kzalloc(sizeof(char), GFP_KERNEL); + ts_data->pdata->fw_version_major = (response[1]<<8) + response[0]; + ts_data->pdata->fw_version_minor = (response[3]<<8) + response[2]; - snprintf(pdata->fw_version, sizeof(char) * 7, "%03d%03d", - pdata->fw_version_major, - pdata->fw_version_minor); + dev_info(&ts_data->client->dev, "major num = %d, minor num = %d\n", + ts_data->pdata->fw_version_major, + ts_data->pdata->fw_version_minor); - return ret; + return 0; } -static int firmware_erase_c33(enum EMEM_TYPE_t emem_type) +static int firmware_erase_c33(struct msg21xx_ts_data *ts_data, + enum EMEM_TYPE_t emem_type) { /* stop mcu */ - write_reg(0x0F, 0xE6, 0x0001); + write_reg(ts_data, 0x0F, 0xE6, 0x0001); /* disable watch dog */ - write_reg_8bit(0x3C, 0x60, 0x55); - write_reg_8bit(0x3C, 0x61, 0xAA); + write_reg_8bit(ts_data, 0x3C, 0x60, 0x55); + write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA); /* set PROGRAM password */ - write_reg_8bit(0x16, 0x1A, 0xBA); - write_reg_8bit(0x16, 0x1B, 0xAB); + write_reg_8bit(ts_data, 0x16, 0x1A, 0xBA); + write_reg_8bit(ts_data, 0x16, 0x1B, 0xAB); - write_reg_8bit(0x16, 0x18, 0x80); + write_reg_8bit(ts_data, 0x16, 0x18, 0x80); if (emem_type == EMEM_ALL) - write_reg_8bit(0x16, 0x08, 0x10); + write_reg_8bit(ts_data, 0x16, 0x08, 0x10); - write_reg_8bit(0x16, 0x18, 0x40); + write_reg_8bit(ts_data, 0x16, 0x18, 0x40); msleep(20); /* clear pce */ @@ -487,7 +469,7 @@ static int firmware_erase_c33(enum EMEM_TYPE_t emem_type) /* erase trigger */ if (emem_type == EMEM_MAIN) - write_reg_8bit(0x16, 0x0E, 0x04); /* erase main */ + write_reg_8bit(ts_data, 0x16, 0x0E, 0x04); /* erase main */ else write_reg_8bit(0x16, 0x0E, 0x08); /* erase all block */ @@ -513,7 +495,9 @@ static int check_fw_update(void) } } return ret; + write_reg_8bit(ts_data, 0x16, 0x0E, 0x08); /* erase all block */ + return 0; } static ssize_t firmware_update_c33(struct device *dev, @@ -526,71 +510,85 @@ static ssize_t firmware_update_c33(struct device *dev, unsigned int crc_info, crc_info_tp; unsigned short reg_data = 0; int update_pass = 1; + bool fw_upgrade = false; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); crc_main = 0xffffffff; crc_info = 0xffffffff; - reset_hw(); + msg21xx_reset_hw(ts_data->pdata); - if (!check_fw_update() && !isForce) { - DBG("****no need to update\n"); - reset_hw(); - FwDataCnt = 0; + msg21xx_read_firmware_id(ts_data); + _ReadBinConfig(ts_data); + if ((main_sw_id == info_sw_id) && + (_CalMainCRC32(ts_data) == bin_conf_crc32) && + (fw_file_major == ts_data->pdata->fw_version_major) && + (fw_file_minor > ts_data->pdata->fw_version_minor)) { + fw_upgrade = true; + } + + if (!fw_upgrade && !isForce) { + dev_dbg(dev, "no need to update\n"); + msg21xx_reset_hw(ts_data->pdata); return size; } - reset_hw(); + msg21xx_reset_hw(ts_data->pdata); msleep(300); - dbbusDWIICEnterSerialDebugMode(); - dbbusDWIICStopMCU(); - dbbusDWIICIICUseBus(); - dbbusDWIICIICReshape(); + dbbusDWIICEnterSerialDebugMode(ts_data); + dbbusDWIICStopMCU(ts_data); + dbbusDWIICIICUseBus(ts_data); + dbbusDWIICIICReshape(ts_data); msleep(300); /* erase main */ - firmware_erase_c33(EMEM_MAIN); + firmware_erase_c33(ts_data, EMEM_MAIN); msleep(1000); - reset_hw(); - dbbusDWIICEnterSerialDebugMode(); - dbbusDWIICStopMCU(); - dbbusDWIICIICUseBus(); - dbbusDWIICIICReshape(); + msg21xx_reset_hw(ts_data->pdata); + dbbusDWIICEnterSerialDebugMode(ts_data); + dbbusDWIICStopMCU(ts_data); + dbbusDWIICIICUseBus(ts_data); + dbbusDWIICIICReshape(ts_data); msleep(300); - + /* + * Program + */ /* polling 0x3CE4 is 0x1C70 */ if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) { do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0x1C70); } switch (emem_type) { case EMEM_ALL: - write_reg(0x3C, 0xE4, 0xE38F); /* for all-blocks */ + write_reg(ts_data, 0x3C, 0xE4, 0xE38F); /* for all-blocks */ break; case EMEM_MAIN: - write_reg(0x3C, 0xE4, 0x7731); /* for main block */ + write_reg(ts_data, 0x3C, 0xE4, 0x7731); /* for main block */ break; case EMEM_INFO: - write_reg(0x3C, 0xE4, 0x7731); /* for info block */ + write_reg(ts_data, 0x3C, 0xE4, 0x7731); /* for info block */ - write_reg_8bit(0x0F, 0xE6, 0x01); + write_reg_8bit(ts_data, 0x0F, 0xE6, 0x01); - write_reg_8bit(0x3C, 0xE4, 0xC5); - write_reg_8bit(0x3C, 0xE5, 0x78); + write_reg_8bit(ts_data, 0x3C, 0xE4, 0xC5); + write_reg_8bit(ts_data, 0x3C, 0xE5, 0x78); - write_reg_8bit(MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x9F); - write_reg_8bit(MSTAR_CHIPTOP_REGISTER_BANK, 0x05, 0x82); + write_reg_8bit(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, + 0x04, 0x9F); + write_reg_8bit(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, + 0x05, 0x82); - write_reg_8bit(0x0F, 0xE6, 0x00); + write_reg_8bit(ts_data, 0x0F, 0xE6, 0x00); msleep(100); break; } /* polling 0x3CE4 is 0x2F43 */ do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0x2F43); /* calculate CRC 32 */ @@ -612,28 +610,28 @@ static ssize_t firmware_update_c33(struct device *dev, } for (j = 0; j < 8; j++) - write_i2c_seq(ts_data->client->addr, + write_i2c_seq(ts_data, ts_data->client->addr, &fw_bin_data[i][j * 128], 128); msleep(100); /* polling 0x3CE4 is 0xD0BC */ do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0xD0BC); - write_reg(0x3C, 0xE4, 0x2F43); + write_reg(ts_data, 0x3C, 0xE4, 0x2F43); } if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) { /* write file done and check crc */ - write_reg(0x3C, 0xE4, 0x1380); + write_reg(ts_data, 0x3C, 0xE4, 0x1380); } msleep(20); if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) { /* polling 0x3CE4 is 0x9432 */ do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0x9432); } @@ -642,12 +640,14 @@ static ssize_t firmware_update_c33(struct device *dev, if ((emem_type == EMEM_ALL) || (emem_type == EMEM_MAIN)) { /* CRC Main from TP */ - crc_main_tp = read_reg(0x3C, 0x80); - crc_main_tp = (crc_main_tp << 16) | read_reg(0x3C, 0x82); + crc_main_tp = read_reg(ts_data, 0x3C, 0x80); + crc_main_tp = (crc_main_tp << 16) | + read_reg(ts_data, 0x3C, 0x82); /* CRC Info from TP */ - crc_info_tp = read_reg(0x3C, 0xA0); - crc_info_tp = (crc_info_tp << 16) | read_reg(0x3C, 0xA2); + crc_info_tp = read_reg(ts_data, 0x3C, 0xA0); + crc_info_tp = (crc_info_tp << 16) | + read_reg(ts_data, 0x3C, 0xA2); } update_pass = 1; @@ -657,94 +657,93 @@ static ssize_t firmware_update_c33(struct device *dev, } if (!update_pass) { - DBG("update_C33 failed\n"); - reset_hw(); - FwDataCnt = 0; + dev_err(dev, "update_C33 failed\n"); + msg21xx_reset_hw(ts_data->pdata); return 0; } - DBG("update_C33 OK\n"); - reset_hw(); - FwDataCnt = 0; + dev_dbg(dev, "update_C33 OK\n"); + msg21xx_reset_hw(ts_data->pdata); return size; } -static unsigned int _CalMainCRC32(void) +static unsigned int _CalMainCRC32(struct msg21xx_ts_data *ts_data) { unsigned int ret = 0; unsigned short reg_data = 0; - reset_hw(); + msg21xx_reset_hw(ts_data->pdata); - dbbusDWIICEnterSerialDebugMode(); - dbbusDWIICStopMCU(); - dbbusDWIICIICUseBus(); - dbbusDWIICIICReshape(); + dbbusDWIICEnterSerialDebugMode(ts_data); + dbbusDWIICStopMCU(ts_data); + dbbusDWIICIICUseBus(ts_data); + dbbusDWIICIICReshape(ts_data); msleep(100); /* Stop MCU */ - write_reg(0x0F, 0xE6, 0x0001); + write_reg(ts_data, 0x0F, 0xE6, 0x0001); /* Stop Watchdog */ - write_reg_8bit(0x3C, 0x60, 0x55); - write_reg_8bit(0x3C, 0x61, 0xAA); + write_reg_8bit(ts_data, 0x3C, 0x60, 0x55); + write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA); /* cmd */ - write_reg(0x3C, 0xE4, 0xDF4C); - write_reg(0x1E, 0x04, 0x7d60); + write_reg(ts_data, 0x3C, 0xE4, 0xDF4C); + write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x7d60); /* TP SW reset */ - write_reg(0x1E, 0x04, 0x829F); + write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x829F); /* MCU run */ - write_reg(0x0F, 0xE6, 0x0000); + write_reg(ts_data, 0x0F, 0xE6, 0x0000); /* polling 0x3CE4 */ do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0x9432); /* Cal CRC Main from TP */ - ret = read_reg(0x3C, 0x80); - ret = (ret << 16) | read_reg(0x3C, 0x82); + ret = read_reg(ts_data, 0x3C, 0x80); + ret = (ret << 16) | read_reg(ts_data, 0x3C, 0x82); - DBG("[21xxA]:Current main crc32=0x%x\n", ret); + dev_dbg(&ts_data->client->dev, + "[21xxA]:Current main crc32=0x%x\n", ret); return ret; } -static void _ReadBinConfig(void) +static void _ReadBinConfig(struct msg21xx_ts_data *ts_data) { unsigned char dbbus_tx_data[5] = {0}; unsigned char dbbus_rx_data[4] = {0}; unsigned short reg_data = 0; - reset_hw(); + msg21xx_reset_hw(ts_data->pdata); - dbbusDWIICEnterSerialDebugMode(); - dbbusDWIICStopMCU(); - dbbusDWIICIICUseBus(); - dbbusDWIICIICReshape(); + dbbusDWIICEnterSerialDebugMode(ts_data); + dbbusDWIICStopMCU(ts_data); + dbbusDWIICIICUseBus(ts_data); + dbbusDWIICIICReshape(ts_data); msleep(100); /* Stop MCU */ - write_reg(0x0F, 0xE6, 0x0001); + write_reg(ts_data, 0x0F, 0xE6, 0x0001); /* Stop Watchdog */ - write_reg_8bit(0x3C, 0x60, 0x55); - write_reg_8bit(0x3C, 0x61, 0xAA); + write_reg_8bit(ts_data, 0x3C, 0x60, 0x55); + write_reg_8bit(ts_data, 0x3C, 0x61, 0xAA); /* cmd */ - write_reg(0x3C, 0xE4, 0xA4AB); - write_reg(MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x7d60); + write_reg(ts_data, 0x3C, 0xE4, 0xA4AB); + write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x7d60); + /* TP SW reset */ - write_reg(0x1E, 0x04, 0x829F); - /* TP SW reset*/ + write_reg(ts_data, MSTAR_CHIPTOP_REGISTER_BANK, 0x04, 0x829F); /* MCU run */ - write_reg(0x0F, 0xE6, 0x0000); + write_reg(ts_data, 0x0F, 0xE6, 0x0000); /* polling 0x3CE4 */ do { - reg_data = read_reg(0x3C, 0xE4); + reg_data = read_reg(ts_data, 0x3C, 0xE4); } while (reg_data != 0x5B58); dbbus_tx_data[0] = 0x72; @@ -752,8 +751,8 @@ static void _ReadBinConfig(void) dbbus_tx_data[2] = 0x55; dbbus_tx_data[3] = 0x00; dbbus_tx_data[4] = 0x04; - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 5); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 4); + write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5); + read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4); if ((dbbus_rx_data[0] >= 0x30 && dbbus_rx_data[0] <= 0x39) && (dbbus_rx_data[1] >= 0x30 && dbbus_rx_data[1] <= 0x39) && (dbbus_rx_data[2] >= 0x31 && dbbus_rx_data[2] <= 0x39)) { @@ -767,20 +766,20 @@ static void _ReadBinConfig(void) dbbus_tx_data[2] = 0xFC; dbbus_tx_data[3] = 0x00; dbbus_tx_data[4] = 0x04; - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 5); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 4); - bin_conf_crc32 = dbbus_rx_data[0]; - bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[1]; - bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[2]; - bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[3]; + write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5); + read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4); + bin_conf_crc32 = (dbbus_rx_data[0] << 24) | + (dbbus_rx_data[1] << 16) | + (dbbus_rx_data[2] << 8) | + (dbbus_rx_data[3]); dbbus_tx_data[0] = 0x72; dbbus_tx_data[1] = 0x83; dbbus_tx_data[2] = 0x00; dbbus_tx_data[3] = 0x00; dbbus_tx_data[4] = 0x04; - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 5); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 4); + write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 5); + read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4); if ((dbbus_rx_data[0] >= 0x30 && dbbus_rx_data[0] <= 0x39) && (dbbus_rx_data[1] >= 0x30 && dbbus_rx_data[1] <= 0x39) && (dbbus_rx_data[2] >= 0x31 && dbbus_rx_data[2] <= 0x39)) { @@ -789,18 +788,18 @@ static void _ReadBinConfig(void) (dbbus_rx_data[2] - 0x30); } - DBG("[21xxA]:main_sw_id = %d, info_sw_id = %d, bin_conf_crc32=0x%x\n", - main_sw_id, info_sw_id, bin_conf_crc32); + dev_dbg(&ts_data->client->dev, + "[21xxA]:main_sw_id = %d, info_sw_id = %d, bin_conf_crc32 = 0x%x\n", + main_sw_id, info_sw_id, bin_conf_crc32); } static ssize_t firmware_update_show(struct device *dev, struct device_attribute *attr, char *buf) { - DBG("*** firmware_update_show() pdata->fw_version = %s ***\n", - pdata->fw_version); + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - return snprintf(buf, sizeof(char) - 1, "%s\n", pdata->fw_version); + return snprintf(buf, 3, "%d\n", ts_data->pdata->updating_fw); } static ssize_t firmware_update_store(struct device *dev, @@ -808,14 +807,15 @@ static ssize_t firmware_update_store(struct device *dev, const char *buf, size_t size) { - bFwUpdating = 1; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); + + ts_data->pdata->updating_fw = true; disable_irq(ts_data->client->irq); - DBG("*** update fw size = %d ***\n", FwDataCnt); size = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, false); enable_irq(ts_data->client->irq); - bFwUpdating = 0; + ts_data->pdata->updating_fw = false; return size; } @@ -830,27 +830,24 @@ static int prepare_fw_data(struct device *dev) int i; int ret; const struct firmware *fw = NULL; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - ret = request_firmware(&fw, pdata->fw_name, dev); + ret = request_firmware(&fw, ts_data->pdata->fw_name, dev); if (ret < 0) { dev_err(dev, "Request firmware failed - %s (%d)\n", - pdata->fw_name, ret); + ts_data->pdata->fw_name, ret); return ret; } - DBG("*** prepare_fw_data() ret = %d, size = %d***\n", ret, fw->size); count = fw->size / 1024; - for (i = 0; i < count; i++) { - memcpy(fw_bin_data[FwDataCnt], fw->data + (i * 1024), 1024); - FwDataCnt++; - } - update_bin_major = (fw->data[0x7f4f] << 8) + fw->data[0x7f4e]; - update_bin_minor = (fw->data[0x7f51] << 8) + fw->data[0x7f50]; - DBG("*** prepare_fw_data bin major = %d ***\n", update_bin_major); - DBG("*** prepare_fw_data bin minor = %d ***\n", update_bin_minor); + for (i = 0; i < count; i++) + memcpy(fw_bin_data[i], fw->data + (i * 1024), 1024); - DBG("***FwDataCnt = %d ***\n", FwDataCnt); + fw_file_major = MSG_FW_FILE_MAJOR_VERSION(fw); + fw_file_minor = MSG_FW_FILE_MINOR_VERSION(fw); + dev_dbg(dev, "New firmware: %d.%d", + fw_file_major, fw_file_minor); return fw->size; } @@ -861,22 +858,22 @@ static ssize_t firmware_update_smart_store(struct device *dev, size_t size) { int ret; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); ret = prepare_fw_data(dev); if (ret < 0) { dev_err(dev, "Request firmware failed -(%d)\n", ret); return ret; } - bFwUpdating = 1; + ts_data->pdata->updating_fw = true; disable_irq(ts_data->client->irq); - DBG("*** update fw size = %d ***\n", FwDataCnt); ret = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, false); if (ret == 0) - DBG("*** firmware_update_c33 ret = %d ***\n", ret); + dev_err(dev, "firmware_update_c33 ret = %d\n", ret); enable_irq(ts_data->client->irq); - bFwUpdating = 0; + ts_data->pdata->updating_fw = false; return ret; } @@ -887,22 +884,22 @@ static ssize_t firmware_force_update_smart_store(struct device *dev, size_t size) { int ret; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); ret = prepare_fw_data(dev); if (ret < 0) { dev_err(dev, "Request firmware failed -(%d)\n", ret); return ret; } - bFwUpdating = 1; + ts_data->pdata->updating_fw = true; disable_irq(ts_data->client->irq); - DBG("*** update fw size = %d ***\n", FwDataCnt); ret = firmware_update_c33(dev, attr, buf, size, EMEM_MAIN, true); if (ret == 0) - DBG("*** firmware_update_c33 et = %d ***\n", ret); + dev_err(dev, "firmware_update_c33 et = %d\n", ret); enable_irq(ts_data->client->irq); - bFwUpdating = 0; + ts_data->pdata->updating_fw = false; return ret; } @@ -919,44 +916,40 @@ static ssize_t firmware_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - msg21xx_read_firmware_id(); - return snprintf(buf, 8, "%s\n", pdata->fw_version); -} - -static ssize_t firmware_version_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t size) -{ - msg21xx_read_firmware_id(); - DBG("*** firmware_version_store() pdata->fw_version = %s ***\n", - pdata->fw_version); + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - return size; + msg21xx_read_firmware_id(ts_data); + return snprintf(buf, sizeof(char) * 8, "%03d%03d\n", + ts_data->pdata->fw_version_major, + ts_data->pdata->fw_version_minor); } -static DEVICE_ATTR(version, (S_IRUGO | S_IWUSR), +static DEVICE_ATTR(version, S_IRUGO, firmware_version_show, - firmware_version_store); + NULL); static ssize_t msg21xx_fw_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, MSTAR_FW_NAME_MAX_LEN - 1, "%s\n", pdata->fw_name); + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); + + return snprintf(buf, MSTAR_FW_NAME_MAX_LEN - 1, + "%s\n", ts_data->pdata->fw_name); } static ssize_t msg21xx_fw_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); if (size > MSTAR_FW_NAME_MAX_LEN - 1) return -EINVAL; - strlcpy(pdata->fw_name, buf, size); - if (pdata->fw_name[size - 1] == '\n') - pdata->fw_name[size - 1] = 0; + strlcpy(ts_data->pdata->fw_name, buf, size); + if (ts_data->pdata->fw_name[size - 1] == '\n') + ts_data->pdata->fw_name[size - 1] = 0; return size; } @@ -964,15 +957,6 @@ static ssize_t msg21xx_fw_name_store(struct device *dev, static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR), msg21xx_fw_name_show, msg21xx_fw_name_store); -static ssize_t firmware_data_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - DBG("*** firmware_data_show() FwDataCnt = %d ***\n", FwDataCnt); - - return FwDataCnt; -} - static ssize_t firmware_data_store(struct device *dev, struct device_attribute *attr, const char *buf, @@ -981,31 +965,26 @@ static ssize_t firmware_data_store(struct device *dev, int count = size / 1024; int i; - for (i = 0; i < count; i++) { - memcpy(fw_bin_data[FwDataCnt], buf + (i * 1024), 1024); - - FwDataCnt++; - } - - DBG("***FwDataCnt = %d ***\n", FwDataCnt); + for (i = 0; i < count; i++) + memcpy(fw_bin_data[i], buf + (i * 1024), 1024); if (buf != NULL) - DBG("*** buf[0] = %c ***\n", buf[0]); + dev_dbg(dev, "buf[0] = %c\n", buf[0]); return size; } -static DEVICE_ATTR(data, (S_IRUGO | S_IWUSR), - firmware_data_show, firmware_data_store); +static DEVICE_ATTR(data, S_IWUSR, NULL, firmware_data_store); -#ifdef TP_PRINT static ssize_t tp_print_show(struct device *dev, struct device_attribute *attr, char *buf) { - tp_print_proc_read(); + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - return snprintf(buf, sizeof(char) - 1, "%d\n", ts_data->suspended); + tp_print_proc_read(ts_data); + + return snprintf(buf, 3, "%d\n", ts_data->suspended); } static ssize_t tp_print_store(struct device *dev, @@ -1013,21 +992,17 @@ static ssize_t tp_print_store(struct device *dev, const char *buf, size_t size) { - DBG("*** tp_print_store() ***\n"); - return size; } static DEVICE_ATTR(tpp, (S_IRUGO | S_IWUSR), tp_print_show, tp_print_store); -#endif #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR static void _msg_enable_proximity(void) { unsigned char tx_data[4] = {0}; - DBG("_msg_enable_proximity!"); tx_data[0] = 0x52; tx_data[1] = 0x00; tx_data[2] = 0x47; @@ -1043,7 +1018,6 @@ static void _msg_disable_proximity(void) { unsigned char tx_data[4] = {0}; - DBG("_msg_disable_proximity!"); tx_data[0] = 0x52; tx_data[1] = 0x00; tx_data[2] = 0x47; @@ -1070,15 +1044,15 @@ static int tsps_msg21xx_data(void) } #endif -static int msg21xx_pinctrl_init(void) +static int msg21xx_pinctrl_init(struct msg21xx_ts_data *ts_data) { int retval; /* Get pinctrl if target uses pinctrl */ - ts_data->ts_pinctrl = devm_pinctrl_get(&(i2c_client->dev)); + ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev)); if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) { retval = PTR_ERR(ts_data->ts_pinctrl); - dev_dbg(&i2c_client->dev, + dev_dbg(&ts_data->client->dev, "Target does not use pinctrl %d\n", retval); goto err_pinctrl_get; } @@ -1087,7 +1061,7 @@ static int msg21xx_pinctrl_init(void) ts_data->ts_pinctrl, PINCTRL_STATE_ACTIVE); if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) { retval = PTR_ERR(ts_data->pinctrl_state_active); - dev_dbg(&i2c_client->dev, + dev_dbg(&ts_data->client->dev, "Can't lookup %s pinstate %d\n", PINCTRL_STATE_ACTIVE, retval); goto err_pinctrl_lookup; @@ -1097,7 +1071,7 @@ static int msg21xx_pinctrl_init(void) ts_data->ts_pinctrl, PINCTRL_STATE_SUSPEND); if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) { retval = PTR_ERR(ts_data->pinctrl_state_suspend); - dev_dbg(&i2c_client->dev, + dev_dbg(&ts_data->client->dev, "Can't lookup %s pinstate %d\n", PINCTRL_STATE_SUSPEND, retval); goto err_pinctrl_lookup; @@ -1107,7 +1081,7 @@ static int msg21xx_pinctrl_init(void) ts_data->ts_pinctrl, PINCTRL_STATE_RELEASE); if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { retval = PTR_ERR(ts_data->pinctrl_state_release); - dev_dbg(&i2c_client->dev, + dev_dbg(&ts_data->client->dev, "Can't lookup %s pinstate %d\n", PINCTRL_STATE_RELEASE, retval); } @@ -1131,7 +1105,7 @@ static unsigned char calculate_checksum(unsigned char *msg, int length) return (unsigned char)((-checksum) & 0xFF); } -static int parse_info(struct touchInfo_t *info) +static int parse_info(struct msg21xx_ts_data *ts_data) { unsigned char data[DEMO_MODE_PACKET_LENGTH] = {0}; unsigned char checksum = 0; @@ -1140,33 +1114,36 @@ static int parse_info(struct touchInfo_t *info) unsigned int delta_x = 0, delta_y = 0; mutex_lock(&msg21xx_mutex); - read_i2c_seq(ts_data->client->addr, &data[0], DEMO_MODE_PACKET_LENGTH); + read_i2c_seq(ts_data, ts_data->client->addr, &data[0], + DEMO_MODE_PACKET_LENGTH); mutex_unlock(&msg21xx_mutex); checksum = calculate_checksum(&data[0], (DEMO_MODE_PACKET_LENGTH-1)); - DBG("check sum: [%x] == [%x]?\n", + dev_dbg(&ts_data->client->dev, "check sum: [%x] == [%x]?\n", data[DEMO_MODE_PACKET_LENGTH-1], checksum); if (data[DEMO_MODE_PACKET_LENGTH-1] != checksum) { - DBG("WRONG CHECKSUM\n"); + dev_err(&ts_data->client->dev, "WRONG CHECKSUM\n"); return -EINVAL; } if (data[0] != 0x52) { - DBG("WRONG HEADER\n"); + dev_err(&ts_data->client->dev, "WRONG HEADER\n"); return -EINVAL; } - info->keycode = 0xFF; + ts_data->info.keycode = 0xFF; if ((data[1] == 0xFF) && (data[2] == 0xFF) && (data[3] == 0xFF) && (data[4] == 0xFF) && (data[6] == 0xFF)) { if ((data[5] == 0xFF) || (data[5] == 0)) { - info->keycode = 0xFF; + ts_data->info.keycode = 0xFF; } else if ((data[5] == 1) || (data[5] == 2) || (data[5] == 4) || (data[5] == 8)) { - info->keycode = data[5] >> 1; + ts_data->info.keycode = data[5] >> 1; - DBG("info->keycode index %d\n", info->keycode); + dev_dbg(&ts_data->client->dev, + "ts_data->info.keycode index %d\n", + ts_data->info.keycode); } #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR else if (bEnableTpProximity && ((data[5] == 0x80) || @@ -1180,7 +1157,7 @@ static int parse_info(struct touchInfo_t *info) } #endif else { - DBG("WRONG KEY\n"); + dev_err(&ts_data->client->dev, "WRONG KEY\n"); return -EINVAL; } } else { @@ -1190,9 +1167,11 @@ static int parse_info(struct touchInfo_t *info) delta_y = (((data[4] & 0x0F) << 8) | data[6]); if ((delta_x == 0) && (delta_y == 0)) { - info->point[0].x = x * pdata->x_max / TPD_WIDTH; - info->point[0].y = y * pdata->y_max / TPD_HEIGHT; - info->count = 1; + ts_data->info.point[0].x = + x * ts_data->pdata->x_max / TPD_WIDTH; + ts_data->info.point[0].y = + y * ts_data->pdata->y_max / TPD_HEIGHT; + ts_data->info.count = 1; } else { if (delta_x > 2048) delta_x -= 4096; @@ -1204,31 +1183,34 @@ static int parse_info(struct touchInfo_t *info) (signed short)delta_x); y2 = (unsigned int)((signed short)y + (signed short)delta_y); - info->point[0].x = x * pdata->x_max / TPD_WIDTH; - info->point[0].y = y * pdata->y_max / TPD_HEIGHT; - info->point[1].x = x2 * pdata->x_max / TPD_WIDTH; - info->point[1].y = y2 * pdata->y_max / TPD_HEIGHT; - info->count = 2; + ts_data->info.point[0].x = + x * ts_data->pdata->x_max / TPD_WIDTH; + ts_data->info.point[0].y = + y * ts_data->pdata->y_max / TPD_HEIGHT; + ts_data->info.point[1].x = + x2 * ts_data->pdata->x_max / TPD_WIDTH; + ts_data->info.point[1].y = + y2 * ts_data->pdata->y_max / TPD_HEIGHT; + ts_data->info.count = ts_data->pdata->num_max_touches; } } return 0; } -static void touch_driver_touch_released(void) +static void touch_driver_touch_released(struct msg21xx_ts_data *ts_data) { int i; - DBG("point touch released\n"); - - for (i = 0; i < MAX_TOUCH_NUM; i++) { - input_mt_slot(input_dev, i); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + for (i = 0; i < ts_data->pdata->num_max_touches; i++) { + input_mt_slot(ts_data->input_dev, i); + input_mt_report_slot_state(ts_data->input_dev, + MT_TOOL_FINGER, 0); } - input_report_key(input_dev, BTN_TOUCH, 0); - input_report_key(input_dev, BTN_TOOL_FINGER, 0); - input_sync(input_dev); + input_report_key(ts_data->input_dev, BTN_TOUCH, 0); + input_report_key(ts_data->input_dev, BTN_TOOL_FINGER, 0); + input_sync(ts_data->input_dev); } /* read data through I2C then report data to input @@ -1236,62 +1218,62 @@ static void touch_driver_touch_released(void) */ static irqreturn_t msg21xx_ts_interrupt(int irq, void *dev_id) { - struct touchInfo_t info; int i = 0; static int last_keycode = 0xFF; static int last_count; - - DBG("touch_driver_do_work()\n"); - - memset(&info, 0x0, sizeof(info)); - if (parse_info(&info) == 0) { - #ifdef CONFIG_TP_HAVE_KEY - if (info.keycode != 0xFF) { /* key touch pressed */ - if (info.keycode < num_buttons) { - if (info.keycode != last_keycode) { - DBG("key touch pressed"); - - input_report_key(input_dev, + struct msg21xx_ts_data *ts_data = dev_id; + + ts_data->info.count = 0; + if (parse_info(ts_data) == 0) { + if (ts_data->info.keycode != 0xFF) { /* key touch pressed */ + if (ts_data->info.keycode < + ts_data->pdata->num_buttons) { + if (ts_data->info.keycode != last_keycode) { + dev_dbg(&ts_data->client->dev, + "key touch pressed"); + + input_report_key(ts_data->input_dev, BTN_TOUCH, 1); - input_report_key(input_dev, - button_map[info.keycode], 1); + input_report_key(ts_data->input_dev, + ts_data->pdata->button_map[ + ts_data->info.keycode], 1); - last_keycode = info.keycode; + last_keycode = ts_data->info.keycode; } else { /* pass duplicate key-pressing */ - DBG("REPEATED KEY\n"); + dev_dbg(&ts_data->client->dev, + "REPEATED KEY\n"); } } else { - DBG("WRONG KEY\n"); + dev_dbg(&ts_data->client->dev, "WRONG KEY\n"); } } else { /* key touch released */ if (last_keycode != 0xFF) { - DBG("key touch released"); + dev_dbg(&ts_data->client->dev, "key touch released"); - input_report_key(input_dev, + input_report_key(ts_data->input_dev, BTN_TOUCH, 0); - input_report_key(input_dev, - button_map[last_keycode], - 0); + input_report_key(ts_data->input_dev, + ts_data->pdata->button_map[last_keycode], + 0); last_keycode = 0xFF; } } - #endif /* CONFIG_TP_HAVE_KEY */ - if (info.count > 0) { /* point touch pressed */ - for (i = 0; i < info.count; i++) { - input_mt_slot(input_dev, i); - input_mt_report_slot_state(input_dev, + if (ts_data->info.count > 0) { /* point touch pressed */ + for (i = 0; i < ts_data->info.count; i++) { + input_mt_slot(ts_data->input_dev, i); + input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, 1); - input_report_abs(input_dev, + input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR, 1); - input_report_abs(input_dev, + input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X, - info.point[i].x); - input_report_abs(input_dev, + ts_data->info.point[i].x); + input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y, - info.point[i].y); + ts_data->info.point[i].y); } } @@ -1299,60 +1281,87 @@ static irqreturn_t msg21xx_ts_interrupt(int irq, void *dev_id) for (i = info.count; i < MAX_TOUCH_NUM; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_state(input_dev, + } + + if (last_count > ts_data->info.count) { + for (i = ts_data->info.count; + i < ts_data->pdata->num_max_touches; + i++) { + input_mt_slot(ts_data->input_dev, i); + input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, 0); } } - last_count = info.count; + last_count = ts_data->info.count; - input_report_key(input_dev, BTN_TOUCH, info.count > 0); - input_report_key(input_dev, BTN_TOOL_FINGER, info.count > 0); + input_report_key(ts_data->input_dev, BTN_TOUCH, + ts_data->info.count > 0); + input_report_key(ts_data->input_dev, BTN_TOOL_FINGER, + ts_data->info.count > 0); - input_sync(input_dev); + input_sync(ts_data->input_dev); } return IRQ_HANDLED; } - -static int msg21xx_ts_power_init(void) +static int msg21xx_ts_power_init(struct msg21xx_ts_data *ts_data, bool init) { int rc; - ts_data->vdd = regulator_get(&i2c_client->dev, "vdd"); - if (IS_ERR(ts_data->vdd)) { - rc = PTR_ERR(ts_data->vdd); - dev_err(&i2c_client->dev, - "Regulator get failed vdd rc=%d\n", rc); - return rc; - } + if (init) { + ts_data->vdd = regulator_get(&ts_data->client->dev, + "vdd"); + if (IS_ERR(ts_data->vdd)) { + rc = PTR_ERR(ts_data->vdd); + dev_err(&ts_data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } - if (regulator_count_voltages(ts_data->vdd) > 0) { - rc = regulator_set_voltage(ts_data->vdd, MSTAR_VTG_MIN_UV, - MSTAR_VTG_MAX_UV); - if (rc) { - dev_err(&i2c_client->dev, - "Regulator set_vtg failed vdd rc=%d\n", rc); - goto reg_vdd_put; + if (regulator_count_voltages(ts_data->vdd) > 0) { + rc = regulator_set_voltage(ts_data->vdd, + MSTAR_VTG_MIN_UV, + MSTAR_VTG_MAX_UV); + if (rc) { + dev_err(&ts_data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", + rc); + goto reg_vdd_put; + } } - } - ts_data->vcc_i2c = regulator_get(&i2c_client->dev, "vcc_i2c"); - if (IS_ERR(ts_data->vcc_i2c)) { - rc = PTR_ERR(ts_data->vcc_i2c); - dev_err(&i2c_client->dev, - "Regulator get failed vcc_i2c rc=%d\n", rc); - goto reg_vdd_set_vtg; - } + ts_data->vcc_i2c = regulator_get(&ts_data->client->dev, + "vcc_i2c"); + if (IS_ERR(ts_data->vcc_i2c)) { + rc = PTR_ERR(ts_data->vcc_i2c); + dev_err(&ts_data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } - if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { - rc = regulator_set_voltage(ts_data->vcc_i2c, - MSTAR_I2C_VTG_MIN_UV, - MSTAR_I2C_VTG_MAX_UV); - if (rc) { - dev_err(&i2c_client->dev, - "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); - goto reg_vcc_i2c_put; + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + rc = regulator_set_voltage(ts_data->vcc_i2c, + MSTAR_I2C_VTG_MIN_UV, + MSTAR_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&ts_data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } } + } else { + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, + MSTAR_VTG_MAX_UV); + + regulator_put(ts_data->vdd); + + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) + regulator_set_voltage(ts_data->vcc_i2c, 0, + MSTAR_I2C_VTG_MAX_UV); + + regulator_put(ts_data->vcc_i2c); } return 0; @@ -1367,59 +1376,42 @@ reg_vdd_put: return rc; } - -static int msg21xx_ts_power_deinit(void) -{ - if (regulator_count_voltages(ts_data->vdd) > 0) - regulator_set_voltage(ts_data->vdd, 0, MSTAR_VTG_MAX_UV); - - regulator_put(ts_data->vdd); - - if (regulator_count_voltages(ts_data->vcc_i2c) > 0) - regulator_set_voltage(ts_data->vcc_i2c, 0, - MSTAR_I2C_VTG_MAX_UV); - - regulator_put(ts_data->vcc_i2c); - return 0; -} - -static int msg21xx_ts_power_on(void) +static int msg21xx_ts_power_on(struct msg21xx_ts_data *ts_data, bool on) { int rc; + if (!on) + goto power_off; + rc = regulator_enable(ts_data->vdd); if (rc) { - dev_err(&i2c_client->dev, + dev_err(&ts_data->client->dev, "Regulator vdd enable failed rc=%d\n", rc); return rc; } rc = regulator_enable(ts_data->vcc_i2c); if (rc) { - dev_err(&i2c_client->dev, + dev_err(&ts_data->client->dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); regulator_disable(ts_data->vdd); } return rc; -} - -static int msg21xx_ts_power_off(void) -{ - int rc; DBG("*** %s ***\n", __func__); rc = regulator_disable(vdd); +power_off: rc = regulator_disable(ts_data->vdd); if (rc) { - dev_err(&i2c_client->dev, + dev_err(&ts_data->client->dev, "Regulator vdd disable failed rc=%d\n", rc); return rc; } rc = regulator_disable(ts_data->vcc_i2c); if (rc) { - dev_err(&i2c_client->dev, + dev_err(&ts_data->client->dev, "Regulator vcc_i2c disable failed rc=%d\n", rc); rc = regulator_enable(ts_data->vdd); } @@ -1427,57 +1419,42 @@ static int msg21xx_ts_power_off(void) return rc; } -static int msg21xx_ts_gpio_configure(bool on) +static int msg21xx_ts_gpio_configure(struct msg21xx_ts_data *ts_data, bool on) { int ret = 0; - if (on) { - if (gpio_is_valid(pdata->irq_gpio)) { - ret = gpio_request(pdata->irq_gpio, "msg21xx_irq_gpio"); - if (ret) { - dev_err(&i2c_client->dev, - "Failed to request GPIO[%d], %d\n", - pdata->irq_gpio, ret); - goto err_irq_gpio_req; - } - ret = gpio_direction_input(pdata->irq_gpio); - if (ret) { - dev_err(&i2c_client->dev, - "Failed to set direction for gpio[%d], %d\n", - pdata->irq_gpio, ret); - goto err_irq_gpio_dir; - } - gpio_set_value_cansleep(pdata->irq_gpio, 1); - } else { - dev_err(&i2c_client->dev, "irq gpio not provided\n"); + if (!on) + goto pwr_deinit; + + if (gpio_is_valid(ts_data->pdata->irq_gpio)) { + ret = gpio_request(ts_data->pdata->irq_gpio, + "msg21xx_irq_gpio"); + if (ret) { + dev_err(&ts_data->client->dev, + "Failed to request GPIO[%d], %d\n", + ts_data->pdata->irq_gpio, ret); goto err_irq_gpio_req; } + ret = gpio_direction_input(ts_data->pdata->irq_gpio); + if (ret) { + dev_err(&ts_data->client->dev, + "Failed to set direction for gpio[%d], %d\n", + ts_data->pdata->irq_gpio, ret); + goto err_irq_gpio_dir; + } + gpio_set_value_cansleep(ts_data->pdata->irq_gpio, 1); + } else { + dev_err(&ts_data->client->dev, "irq gpio not provided\n"); + goto err_irq_gpio_req; + } - if (gpio_is_valid(pdata->reset_gpio)) { - ret = gpio_request(pdata->reset_gpio, - "msg21xx_reset_gpio"); - if (ret) { - dev_err(&i2c_client->dev, - "Failed to request GPIO[%d], %d\n", - pdata->reset_gpio, ret); - goto err_reset_gpio_req; - } - - /* power on TP */ - ret = gpio_direction_output(pdata->reset_gpio, 1); - if (ret) { - dev_err(&i2c_client->dev, - "Failed to set direction for GPIO[%d], %d\n", - pdata->reset_gpio, ret); - goto err_reset_gpio_dir; - } - msleep(100); - gpio_set_value_cansleep(pdata->reset_gpio, 0); - msleep(20); - gpio_set_value_cansleep(pdata->reset_gpio, 1); - msleep(200); - } else { - dev_err(&i2c_client->dev, "reset gpio not provided\n"); + if (gpio_is_valid(ts_data->pdata->reset_gpio)) { + ret = gpio_request(ts_data->pdata->reset_gpio, + "msg21xx_reset_gpio"); + if (ret) { + dev_err(&ts_data->client->dev, + "Failed to request GPIO[%d], %d\n", + ts_data->pdata->reset_gpio, ret); goto err_reset_gpio_req; } @@ -1495,55 +1472,93 @@ static int msg21xx_ts_gpio_configure(bool on) } } return 0; + /* power on TP */ + ret = gpio_direction_output( + ts_data->pdata->reset_gpio, 1); + if (ret) { + dev_err(&ts_data->client->dev, + "Failed to set direction for GPIO[%d], %d\n", + ts_data->pdata->reset_gpio, ret); + goto err_reset_gpio_dir; + } + msleep(100); + gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 0); + msleep(20); + gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 1); + msleep(200); + } else { + dev_err(&ts_data->client->dev, "reset gpio not provided\n"); + goto err_reset_gpio_req; + } + + return 0; + err_reset_gpio_dir: - if (gpio_is_valid(pdata->reset_gpio)) - gpio_free(pdata->irq_gpio); + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->irq_gpio); err_reset_gpio_req: err_irq_gpio_dir: - if (gpio_is_valid(pdata->irq_gpio)) - gpio_free(pdata->irq_gpio); + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); err_irq_gpio_req: return ret; + +pwr_deinit: + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + if (gpio_is_valid(ts_data->pdata->reset_gpio)) { + gpio_set_value_cansleep(ts_data->pdata->reset_gpio, 0); + ret = gpio_direction_input(ts_data->pdata->reset_gpio); + if (ret) + dev_err(&ts_data->client->dev, + "Unable to set direction for gpio [%d]\n", + ts_data->pdata->reset_gpio); + gpio_free(ts_data->pdata->reset_gpio); + } + return 0; } #ifdef CONFIG_PM static int msg21xx_ts_resume(struct device *dev) { int retval; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - mutex_lock(&ts_data->ts_mutex); - if (ts_data->suspended) { - if (ts_data->ts_pinctrl) { - retval = pinctrl_select_state(ts_data->ts_pinctrl, - ts_data->pinctrl_state_active); - if (retval < 0) { - dev_err(dev, "Cannot get active pinctrl state\n"); - mutex_unlock(&ts_data->ts_mutex); - return retval; - } - } + if (!ts_data->suspended) { + dev_info(dev, "msg21xx_ts already in resume\n"); + return 0; + } - retval = msg21xx_ts_gpio_configure(true); - if (retval) { - dev_err(dev, "Failed to put gpios in active state %d", - retval); - mutex_unlock(&ts_data->ts_mutex); - return retval; - } + mutex_lock(&ts_data->ts_mutex); - enable_irq(ts_data->client->irq); + retval = msg21xx_ts_power_on(ts_data, true); + if (retval) { + dev_err(dev, "msg21xx_ts power on failed"); + mutex_unlock(&ts_data->ts_mutex); + return retval; + } - retval = msg21xx_ts_power_on(); - if (retval) { - dev_err(dev, "msg21xx_ts power on failed"); + if (ts_data->ts_pinctrl) { + retval = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_active); + if (retval < 0) { + dev_err(dev, "Cannot get active pinctrl state\n"); mutex_unlock(&ts_data->ts_mutex); return retval; } + } - ts_data->suspended = 0; - } else { - dev_info(dev, "msg21xx_ts already in resume\n"); + retval = msg21xx_ts_gpio_configure(ts_data, true); + if (retval) { + dev_err(dev, "Failed to put gpios in active state %d", + retval); + mutex_unlock(&ts_data->ts_mutex); + return retval; } + + enable_irq(ts_data->client->irq); + ts_data->suspended = false; + mutex_unlock(&ts_data->ts_mutex); return 0; @@ -1552,56 +1567,61 @@ static int msg21xx_ts_resume(struct device *dev) static int msg21xx_ts_suspend(struct device *dev) { int retval; + struct msg21xx_ts_data *ts_data = dev_get_drvdata(dev); - if (bFwUpdating) { - DBG("suspend bFwUpdating=%d\n", bFwUpdating); + if (ts_data->pdata->updating_fw) { + dev_info(dev, "Firmware loading in progress\n"); + return 0; + } + + if (ts_data->suspended) { + dev_info(dev, "msg21xx_ts already in suspend\n"); return 0; } #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR if (bEnableTpProximity) { - DBG("suspend bEnableTpProximity=%d\n", bEnableTpProximity); + dev_dbg(dev, "suspend bEnableTpProximity=%d\n", + bEnableTpProximity); return 0; } #endif mutex_lock(&ts_data->ts_mutex); - if (ts_data->suspended == 0) { - disable_irq(ts_data->client->irq); - - touch_driver_touch_released(); - retval = msg21xx_ts_power_off(); - if (retval) { - dev_err(dev, "msg21xx_ts power off failed"); - mutex_unlock(&ts_data->ts_mutex); - return retval; - } + disable_irq(ts_data->client->irq); - if (ts_data->ts_pinctrl) { - retval = pinctrl_select_state(ts_data->ts_pinctrl, - ts_data->pinctrl_state_suspend); - if (retval < 0) { - dev_err(&i2c_client->dev, "Cannot get idle pinctrl state\n"); - mutex_unlock(&ts_data->ts_mutex); - return retval; - } - } + touch_driver_touch_released(ts_data); - retval = msg21xx_ts_gpio_configure(false); - if (retval) { - dev_err(dev, "Failed to put gpios in idle state %d", - retval); + if (ts_data->ts_pinctrl) { + retval = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_suspend); + if (retval < 0) { + dev_err(dev, "Cannot get idle pinctrl state %d\n", + retval); mutex_unlock(&ts_data->ts_mutex); return retval; } + } - ts_data->suspended = 1; - } else { - dev_err(dev, "msg21xx_ts already in suspend\n"); + retval = msg21xx_ts_gpio_configure(ts_data, false); + if (retval) { + dev_err(dev, "Failed to put gpios in idle state %d", + retval); + mutex_unlock(&ts_data->ts_mutex); + return retval; + } + + retval = msg21xx_ts_power_on(ts_data, false); + if (retval) { + dev_err(dev, "msg21xx_ts power off failed"); + mutex_unlock(&ts_data->ts_mutex); + return retval; } - mutex_unlock(&ts_data->ts_mutex); + ts_data->suspended = true; + + mutex_unlock(&ts_data->ts_mutex); return 0; } @@ -1616,19 +1636,52 @@ static int msg21xx_ts_suspend(struct device *dev) } #endif +static int msg21xx_debug_suspend_set(void *_data, u64 val) +{ + struct msg21xx_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + msg21xx_ts_suspend(&data->client->dev); + else + msg21xx_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int msg21xx_debug_suspend_get(void *_data, u64 *val) +{ + struct msg21xx_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + *val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, msg21xx_debug_suspend_get, + msg21xx_debug_suspend_set, "%lld\n"); + + #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct fb_event *evdata = data; int *blank; + struct msg21xx_ts_data *ts_data = + container_of(self, struct msg21xx_ts_data, fb_notif); if (evdata && evdata->data && event == FB_EVENT_BLANK) { blank = evdata->data; if (*blank == FB_BLANK_UNBLANK) - msg21xx_ts_resume(&i2c_client->dev); + msg21xx_ts_resume(&ts_data->client->dev); else if (*blank == FB_BLANK_POWERDOWN) - msg21xx_ts_suspend(&i2c_client->dev); + msg21xx_ts_suspend(&ts_data->client->dev); } return 0; @@ -1695,6 +1748,20 @@ static int msg21xx_parse_dt(struct device *dev, if (rc) return rc; + rc = of_property_read_u32(np, "mstar,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_reset_delay_ms = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "mstar,post-hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->post_hard_reset_delay_ms = temp_val; + else + return rc; + /* reset, irq gpio info */ pdata->reset_gpio = of_get_named_gpio_flags(np, "mstar,reset-gpio", 0, &pdata->reset_gpio_flags); @@ -1706,16 +1773,27 @@ static int msg21xx_parse_dt(struct device *dev, if (pdata->irq_gpio < 0) return pdata->irq_gpio; + rc = of_property_read_u32(np, "mstar,ic-type", &temp_val); + if (rc && (rc != -EINVAL)) + return rc; + + pdata->ic_type = temp_val; + + rc = of_property_read_u32(np, "mstar,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; prop = of_find_property(np, "mstar,button-map", NULL); if (prop) { - num_buttons = prop->length / sizeof(temp_val); - if (num_buttons > MAX_BUTTONS) + pdata->num_buttons = prop->length / sizeof(temp_val); + if (pdata->num_buttons > MAX_BUTTONS) return -EINVAL; rc = of_property_read_u32_array(np, - "mstar,button-map", button_map, - num_buttons); + "mstar,button-map", pdata->button_map, + pdata->num_buttons); if (rc) { dev_err(dev, "Unable to read key codes\n"); return rc; @@ -1729,12 +1807,11 @@ static int msg21xx_parse_dt(struct device *dev, static int msg21xx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int ret = 0; - - if (input_dev != NULL) { - DBG("input device has found\n"); - return -EINVAL; - } + int ret = 0, i; + struct dentry *temp, *dir; + struct input_dev *input_dev; + struct msg21xx_ts_data *ts_data; + struct msg21xx_ts_platform_data *pdata; if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, @@ -1750,43 +1827,80 @@ static int msg21xx_ts_probe(struct i2c_client *client, } else pdata = client->dev.platform_data; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + ts_data = devm_kzalloc(&client->dev, sizeof(struct msg21xx_ts_data), GFP_KERNEL); if (!ts_data) return -ENOMEM; - DBG("*** %s ***\n", __func__); + ts_data->client = client; + ts_data->info.point = devm_kzalloc(&client->dev, + sizeof(struct touchPoint_t) * pdata->num_max_touches, + GFP_KERNEL); + if (!ts_data->info.point) { + dev_err(&client->dev, "Not enough memory\n"); + return -ENOMEM; + } - i2c_client = client; + /* allocate an input device */ + input_dev = input_allocate_device(); + if (!input_dev) { + ret = -ENOMEM; + dev_err(&client->dev, "input device allocation failed\n"); + goto err_input_allocate_dev; + } - ret = msg21xx_ts_power_init(); - if (ret) + input_dev->name = client->name; + input_dev->phys = "I2C"; + input_dev->dev.parent = &client->dev; + input_dev->id.bustype = BUS_I2C; + + ts_data->input_dev = input_dev; + ts_data->client = client; + ts_data->pdata = pdata; + + input_set_drvdata(input_dev, ts_data); + i2c_set_clientdata(client, ts_data); + + ret = msg21xx_ts_power_init(ts_data, true); + if (ret) { dev_err(&client->dev, "Mstar power init failed\n"); + return ret; + } - ret = msg21xx_ts_power_on(); + ret = msg21xx_ts_power_on(ts_data, true); if (ret) { dev_err(&client->dev, "Mstar power on failed\n"); goto exit_deinit_power; } - ret = msg21xx_pinctrl_init(); + ret = msg21xx_pinctrl_init(ts_data); if (!ret && ts_data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ ret = pinctrl_select_state(ts_data->ts_pinctrl, ts_data->pinctrl_state_active); if (ret < 0) - goto exit_pinctrl_select; - } else { - goto exit_pinctrl_init; + dev_err(&client->dev, + "Failed to select %s pinatate %d\n", + PINCTRL_STATE_ACTIVE, ret); } - ret = msg21xx_ts_gpio_configure(true); + ret = msg21xx_ts_gpio_configure(ts_data, true); if (ret) { dev_err(&client->dev, "Failed to configure gpio %d\n", ret); goto exit_gpio_config; } - if (get_ic_type() == 0) { - pr_err("the currnet ic is not Mstar\n"); + if (msg21xx_get_ic_type(ts_data) == 0) { + dev_err(&client->dev, "The current IC is not Mstar\n"); ret = -1; goto err_wrong_ic_type; } @@ -1794,19 +1908,6 @@ static int msg21xx_ts_probe(struct i2c_client *client, mutex_init(&msg21xx_mutex); mutex_init(&ts_data->ts_mutex); - /* allocate an input device */ - input_dev = input_allocate_device(); - if (!input_dev) { - ret = -ENOMEM; - pr_err("*** input device allocation failed ***\n"); - goto err_input_allocate_dev; - } - - input_dev->name = client->name; - input_dev->phys = "I2C"; - input_dev->dev.parent = &client->dev; - input_dev->id.bustype = BUS_I2C; - /* set the supported event type for input device */ set_bit(EV_ABS, input_dev->evbit); set_bit(EV_SYN, input_dev->evbit); @@ -1815,9 +1916,8 @@ static int msg21xx_ts_probe(struct i2c_client *client, set_bit(BTN_TOOL_FINGER, input_dev->keybit); set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - ts_data->input_dev = input_dev; - ts_data->client = client; - ts_data->pdata = pdata; + for (i = 0; i < pdata->num_buttons; i++) + input_set_capability(input_dev, EV_KEY, pdata->button_map[i]); input_set_drvdata(input_dev, ts_data); i2c_set_clientdata(client, ts_data); @@ -1833,64 +1933,82 @@ static int msg21xx_ts_probe(struct i2c_client *client, input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 2, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 2, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); - ret = input_mt_init_slots(input_dev, MAX_TOUCH_NUM, 0); + ret = input_mt_init_slots(input_dev, pdata->num_max_touches, 0); if (ret) { - pr_err("Error %d initialising slots\n", ret); + dev_err(&client->dev, + "Error %d initialising slots\n", ret); goto err_free_mem; } /* register the input device to input sub-system */ ret = input_register_device(input_dev); if (ret < 0) { - pr_err("*** Unable to register ms-touchscreen input device ***\n"); + dev_err(&client->dev, + "Unable to register ms-touchscreen input device\n"); goto err_input_reg_dev; } - /* set sysfs for firmware */ - firmware_class = class_create(THIS_MODULE, "ms-touchscreen-msg21xx"); - if (IS_ERR(firmware_class)) - pr_err("Failed to create class(firmware)!\n"); - - firmware_cmd_dev = device_create(firmware_class, NULL, 0, - NULL, "device"); - if (IS_ERR(firmware_cmd_dev)) - pr_err("Failed to create device(firmware_cmd_dev)!\n"); - /* version */ - if (device_create_file(firmware_cmd_dev, &dev_attr_version) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_version.attr.name); + if (device_create_file(&client->dev, &dev_attr_version) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_version.attr.name); + goto err_create_fw_ver_file; + } /* update */ - if (device_create_file(firmware_cmd_dev, &dev_attr_update) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_update.attr.name); + if (device_create_file(&client->dev, &dev_attr_update) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_update.attr.name); + goto err_create_fw_update_file; + } /* data */ - if (device_create_file(firmware_cmd_dev, &dev_attr_data) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_data.attr.name); - /* fw name*/ - if (device_create_file(firmware_cmd_dev, &dev_attr_fw_name) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_fw_name.attr.name); - /* smart fw update*/ - if (device_create_file(firmware_cmd_dev, &dev_attr_update_fw) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_update_fw.attr.name); - /* smart fw force update*/ - if (device_create_file(firmware_cmd_dev, &dev_attr_force_update_fw) < 0) - pr_err("Failed to create device file(%s)!\n", - dev_attr_force_update_fw.attr.name); + if (device_create_file(&client->dev, &dev_attr_data) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_data.attr.name); + goto err_create_fw_data_file; + } + /* fw name */ + if (device_create_file(&client->dev, &dev_attr_fw_name) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_fw_name.attr.name); + goto err_create_fw_name_file; + } + /* smart fw update */ + if (device_create_file(&client->dev, &dev_attr_update_fw) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_update_fw.attr.name); + goto err_create_update_fw_file; + } + /* smart fw force update */ + if (device_create_file(&client->dev, + &dev_attr_force_update_fw) < 0) { + dev_err(&client->dev, + "Failed to create device file(%s)!\n", + dev_attr_force_update_fw.attr.name); + goto err_create_force_update_fw_file; + } + dir = debugfs_create_dir(MSTAR_DEBUG_DIR_NAME, NULL); + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, dir, + ts_data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + dev_err(&client->dev, + "debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + goto free_debug_dir; + } #ifdef TP_PRINT - tp_print_create_entry(); + tp_print_create_entry(ts_data); #endif - dev_set_drvdata(firmware_cmd_dev, NULL); - ret = request_threaded_irq(client->irq, NULL, msg21xx_ts_interrupt, pdata->irq_gpio_flags | IRQF_ONESHOT, @@ -1898,7 +2016,7 @@ static int msg21xx_ts_probe(struct i2c_client *client, if (ret) goto err_req_irq; - disable_irq(ts_data->client->irq); + disable_irq(client->irq); #if defined(CONFIG_FB) ts_data->fb_notif.notifier_call = fb_notifier_callback; @@ -1910,91 +2028,38 @@ static int msg21xx_ts_probe(struct i2c_client *client, &tsps_msg21xx_data); #endif -#ifdef FIRMWARE_AUTOUPDATE - get_customer_firmware_version(); - _ReadBinConfig(); - - if (main_sw_id == info_sw_id) { - if (_CalMainCRC32() == bin_conf_crc32) { - if ((main_sw_id >= SWID_START) && - (main_sw_id < SWID_NULL)) { - update_bin_major = (MSG_FIRMWARE - [main_sw_id - SWID_START][0x7f4f] << 8) - + MSG_FIRMWARE[main_sw_id - SWID_START][0x7f4e]; - update_bin_minor = (MSG_FIRMWARE - [main_sw_id - SWID_START][0x7f51] << 8) - + MSG_FIRMWARE[main_sw_id - SWID_START][0x7f50]; - - /* check upgrading */ - if ((update_bin_major == fw_version_major) && - (update_bin_minor > fw_version_minor)) { - update_flag = 1; - } - } - } else { - if ((info_sw_id >= SWID_START) && - (info_sw_id < SWID_NULL)) { - update_bin_major = (MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f4f] << 8) - + MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f4e]; - update_bin_minor = (MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f51] << 8) - + MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f50]; - update_flag = 1; - } - } - } else { - if ((info_sw_id >= SWID_START) && (info_sw_id < SWID_NULL)) { - update_bin_major = (MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f4f] << 8) - + MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f4e]; - update_bin_minor = (MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f51] << 8) - + MSG_FIRMWARE - [info_sw_id - SWID_START][0x7f50]; - update_flag = 1; - } - } - - if (update_flag == 1) { - DBG("MSG21XX_fw_auto_update begin....\n"); - /* transfer data */ - for (i = 0; i < 33; i++) { - firmware_data_store(NULL, NULL, - &(MSG_FIRMWARE[info_sw_id - SWID_START][i * 1024]), - 1024); - } - - kthread_run(fwAutoUpdate, 0, "MSG21XX_fw_auto_update"); - DBG("*** mstar touch screen registered ***\n"); - return 0; - } - - reset_hw(); -#endif - - DBG("*** mstar touch screen registered ***\n"); - enable_irq(ts_data->client->irq); + dev_dbg(&client->dev, "mstar touch screen registered\n"); + enable_irq(client->irq); return 0; err_req_irq: - free_irq(ts_data->client->irq, ts_data); + free_irq(client->irq, ts_data); + device_remove_file(&client->dev, &dev_attr_data); +free_debug_dir: + debugfs_remove_recursive(dir); +err_create_fw_data_file: + device_remove_file(&client->dev, &dev_attr_update); +err_create_fw_update_file: + device_remove_file(&client->dev, &dev_attr_version); +err_create_fw_name_file: + device_remove_file(&client->dev, &dev_attr_fw_name); +err_create_update_fw_file: + device_remove_file(&client->dev, &dev_attr_update_fw); +err_create_force_update_fw_file: + device_remove_file(&client->dev, &dev_attr_force_update_fw); +err_create_fw_ver_file: + input_unregister_device(input_dev); err_input_reg_dev: + input_free_device(input_dev); + input_dev = NULL; err_input_allocate_dev: mutex_destroy(&msg21xx_mutex); mutex_destroy(&ts_data->ts_mutex); - input_unregister_device(input_dev); - input_free_device(input_dev); - input_dev = NULL; err_wrong_ic_type: - msg21xx_ts_gpio_configure(false); + msg21xx_ts_gpio_configure(ts_data, false); exit_gpio_config: -exit_pinctrl_select: if (ts_data->ts_pinctrl) { if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { devm_pinctrl_put(ts_data->ts_pinctrl); @@ -2003,13 +2068,13 @@ exit_pinctrl_select: ret = pinctrl_select_state(ts_data->ts_pinctrl, ts_data->pinctrl_state_release); if (ret < 0) - pr_err("Cannot get release pinctrl state\n"); + dev_err(&ts_data->client->dev, + "Cannot get release pinctrl state\n"); } } -exit_pinctrl_init: - msg21xx_ts_power_off(); + msg21xx_ts_power_on(ts_data, false); exit_deinit_power: - msg21xx_ts_power_deinit(); + msg21xx_ts_power_init(ts_data, false); err_free_mem: input_free_device(input_dev); @@ -2022,12 +2087,11 @@ err_free_mem: static int touch_driver_remove(struct i2c_client *client) { int retval = 0; - - DBG("touch_driver_remove()\n"); + struct msg21xx_ts_data *ts_data = i2c_get_clientdata(client); free_irq(ts_data->client->irq, ts_data); - gpio_free(pdata->irq_gpio); - gpio_free(pdata->reset_gpio); + gpio_free(ts_data->pdata->irq_gpio); + gpio_free(ts_data->pdata->reset_gpio); if (ts_data->ts_pinctrl) { if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { @@ -2037,11 +2101,12 @@ static int touch_driver_remove(struct i2c_client *client) retval = pinctrl_select_state(ts_data->ts_pinctrl, ts_data->pinctrl_state_release); if (retval < 0) - pr_err("Cannot get release pinctrl state\n"); + dev_err(&ts_data->client->dev, + "Cannot get release pinctrl state\n"); } } - input_unregister_device(input_dev); + input_unregister_device(ts_data->input_dev); mutex_destroy(&msg21xx_mutex); mutex_destroy(&ts_data->ts_mutex); @@ -2082,7 +2147,7 @@ module_i2c_driver(touch_device_driver); static unsigned short InfoAddr = 0x0F, PoolAddr = 0x10, TransLen = 256; static unsigned char row, units, cnt; -static int tp_print_proc_read(void) +static int tp_print_proc_read(struct msg21xx_ts_data *ts_data) { unsigned short i, j; unsigned short left, offset = 0; @@ -2106,9 +2171,9 @@ static int tp_print_proc_read(void) & 0xFF; dbbus_tx_data[2] = (PoolAddr + offset) & 0xFF; mutex_lock(&msg21xx_mutex); - write_i2c_seq(ts_data->client->addr, + write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 3); - read_i2c_seq(ts_data->client->addr, + read_i2c_seq(ts_data, ts_data->client->addr, &buf[offset], left > TransLen ? TransLen : left); mutex_unlock(&msg21xx_mutex); @@ -2151,7 +2216,7 @@ static int tp_print_proc_read(void) return 0; } -static void tp_print_create_entry(void) +static void tp_print_create_entry(struct msg21xx_ts_data *ts_data) { unsigned char dbbus_tx_data[3] = {0}; unsigned char dbbus_rx_data[8] = {0}; @@ -2160,8 +2225,8 @@ static void tp_print_create_entry(void) dbbus_tx_data[1] = 0x00; dbbus_tx_data[2] = 0x58; mutex_lock(&msg21xx_mutex); - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 3); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 4); + write_i2c_seq(ts_data, ts_data->client->addr, &dbbus_tx_data[0], 3); + read_i2c_seq(ts_data, ts_data->client->addr, &dbbus_rx_data[0], 4); mutex_unlock(&msg21xx_mutex); InfoAddr = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0]; PoolAddr = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2]; @@ -2172,8 +2237,10 @@ static void tp_print_create_entry(void) dbbus_tx_data[1] = (InfoAddr >> 8) & 0xFF; dbbus_tx_data[2] = InfoAddr & 0xFF; mutex_lock(&msg21xx_mutex); - write_i2c_seq(ts_data->client->addr, &dbbus_tx_data[0], 3); - read_i2c_seq(ts_data->client->addr, &dbbus_rx_data[0], 8); + write_i2c_seq(ts_data, ts_data->client->addr, + &dbbus_tx_data[0], 3); + read_i2c_seq(ts_data, ts_data->client->addr, + &dbbus_rx_data[0], 8); mutex_unlock(&msg21xx_mutex); units = dbbus_rx_data[0]; @@ -2181,10 +2248,10 @@ static void tp_print_create_entry(void) cnt = dbbus_rx_data[2]; TransLen = (dbbus_rx_data[7]<<8) + dbbus_rx_data[6]; - if (device_create_file(firmware_cmd_dev, &dev_attr_tpp) < 0) { - pr_err("Failed to create device file(%s)!\n", + if (device_create_file(&ts_data->client->dev, + &dev_attr_tpp) < 0) + dev_err(&ts_data->client->dev, "Failed to create device file(%s)!\n", dev_attr_tpp.attr.name); - } } } #endif diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index c28b8df4194e..ea8db1a431d0 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -14,6 +14,7 @@ #include <linux/dma-mapping.h> #include <linux/dma-mapping-fast.h> #include <linux/io-pgtable-fast.h> +#include <linux/vmalloc.h> #include <asm/cacheflush.h> #include <asm/dma-iommu.h> @@ -512,6 +513,33 @@ static void fast_smmu_free(struct device *dev, size_t size, __fast_smmu_free_pages(pages, count); } +static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, + size_t size, struct dma_attrs *attrs) +{ + struct vm_struct *area; + unsigned long uaddr = vma->vm_start; + struct page **pages; + int i, nr_pages, ret = 0; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + area = find_vm_area(cpu_addr); + if (!area) + return -EINVAL; + + pages = area->pages; + nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + for (i = vma->vm_pgoff; i < nr_pages && uaddr < vma->vm_end; i++) { + ret = vm_insert_page(vma, uaddr, pages[i]); + if (ret) + break; + uaddr += PAGE_SIZE; + } + + return ret; +} + static int fast_smmu_dma_supported(struct device *dev, u64 mask) { return mask <= 0xffffffff; @@ -559,6 +587,7 @@ static int fast_smmu_notify(struct notifier_block *self, static const struct dma_map_ops fast_smmu_dma_ops = { .alloc = fast_smmu_alloc, .free = fast_smmu_free, + .mmap = fast_smmu_mmap_attrs, .map_page = fast_smmu_map_page, .unmap_page = fast_smmu_unmap_page, .map_sg = fast_smmu_map_sg, diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 291720db72ce..2f7529814cd9 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/delay.h> #include <linux/slab.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -42,8 +43,14 @@ #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) +#define FLASH_LED_REG_THERMAL_THRSH1(base) (base + 0x56) +#define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57) +#define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58) #define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) #define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) +#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) +#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) +#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) #define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0) @@ -55,15 +62,22 @@ #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) +#define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0) +#define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) +#define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) +#define FLASH_LED_THERMAL_OTST_MASK GENMASK(2, 0) +#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(2, 0) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) +#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0) #define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8) #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) #define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25) +#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_WARMUP_DELAY_DEFAULT 2 @@ -74,9 +88,21 @@ #define FLASH_LED_VPH_DROOP_DEBOUNCE_MAX 3 #define FLASH_LED_VPH_DROOP_HYST_MAX 3 #define FLASH_LED_VPH_DROOP_THRESH_MAX 7 +#define FLASH_LED_THERMAL_THRSH_MIN 3 +#define FLASH_LED_THERMAL_OTST_LEVELS 3 +#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000 +#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000 +#define FLASH_LED_RPARA_DEFAULT_UOHM 0 #define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1 #define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) +#define FLASH_LED_LMH_LEVEL_DEFAULT 0 +#define FLASH_LED_LMH_MITIGATION_ENABLE 1 +#define FLASH_LED_LMH_MITIGATION_DISABLE 0 +#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2 +#define FLASH_LED_LMH_MITIGATION_SEL_MAX 2 +#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000 +#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000 #define FLASH_LED_IRES_BASE 3 #define FLASH_LED_IRES_DIVISOR 2500 #define FLASH_LED_IRES_MIN_UA 5000 @@ -96,6 +122,7 @@ #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_MIN_CURRENT_MA 25 +#define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750 /* notifier call chain for flash-led irqs */ static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); @@ -156,17 +183,26 @@ struct flash_switch_data { * Flash LED configuration read from device tree */ struct flash_led_platform_data { + int *thermal_derate_current; int all_ramp_up_done_irq; int all_ramp_down_done_irq; int led_fault_irq; + int ibatt_ocp_threshold_ua; + int vled_max_uv; + int rpara_uohm; + int lmh_rbatt_threshold_uohm; + int lmh_ocv_threshold_uv; u8 isc_delay; u8 warmup_delay; u8 current_derate_en_cfg; u8 vph_droop_threshold; u8 vph_droop_hysteresis; u8 vph_droop_debounce; + u8 lmh_mitigation_sel; + u8 lmh_level; u8 hw_strobe_option; bool hdrm_auto_mode_en; + bool thermal_derate_en; }; /* @@ -185,6 +221,7 @@ struct qpnp_flash_led { int num_snodes; int enable; u16 base; + bool trigger_lmh; }; static int @@ -206,6 +243,20 @@ qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data) } static int +qpnp_flash_led_masked_read(struct qpnp_flash_led *led, u16 addr, u8 mask, + u8 *val) +{ + int rc; + + rc = qpnp_flash_led_read(led, addr, val); + if (rc < 0) + return rc; + + *val &= mask; + return rc; +} + +static int qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val) { @@ -293,6 +344,20 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (rc < 0) return rc; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SEL(led->base), + FLASH_LED_LMH_MITIGATION_SEL_MASK, + led->pdata->lmh_mitigation_sel); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LMH_LEVEL(led->base), + FLASH_LED_LMH_LEVEL_MASK, + led->pdata->lmh_level); + if (rc < 0) + return rc; + return 0; } @@ -364,10 +429,275 @@ out: return rc; } -static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode, - struct qpnp_flash_led *led) +static int get_property_from_fg(struct qpnp_flash_led *led, + enum power_supply_property prop, int *val) +{ + int rc; + union power_supply_propval pval = {0, }; + + if (!led->bms_psy) { + dev_err(&led->pdev->dev, "no bms psy found\n"); + return -EINVAL; + } + + rc = power_supply_get_property(led->bms_psy, prop, &pval); + if (rc) { + dev_err(&led->pdev->dev, + "bms psy doesn't support reading prop %d rc = %d\n", + prop, rc); + return rc; + } + + *val = pval.intval; + return rc; +} + +#define VOLTAGE_HDRM_DEFAULT_MV 350 +static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) +{ + int i, voltage_hdrm_mv = 0, voltage_hdrm_max = 0; + + for (i = 0; i < led->num_fnodes; i++) { + if (led->fnode[i].led_on) { + if (led->fnode[i].id < 2) { + if (led->fnode[i].current_ma < 750) + voltage_hdrm_mv = 125; + else if (led->fnode[i].current_ma < 1000) + voltage_hdrm_mv = 175; + else if (led->fnode[i].current_ma < 1250) + voltage_hdrm_mv = 250; + else + voltage_hdrm_mv = 350; + } else { + if (led->fnode[i].current_ma < 375) + voltage_hdrm_mv = 125; + else if (led->fnode[i].current_ma < 500) + voltage_hdrm_mv = 175; + else if (led->fnode[i].current_ma < 625) + voltage_hdrm_mv = 250; + else + voltage_hdrm_mv = 350; + } + + voltage_hdrm_max = max(voltage_hdrm_max, + voltage_hdrm_mv); + } + } + + if (!voltage_hdrm_max) + return VOLTAGE_HDRM_DEFAULT_MV; + + return voltage_hdrm_max; +} + +#define UCONV 1000000LL +#define MCONV 1000LL +#define FLASH_VDIP_MARGIN 50000 +#define BOB_EFFICIENCY 900LL +#define VIN_FLASH_MIN_UV 3300000LL +static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led) +{ + int ocv_uv, rbatt_uohm, ibat_now, voltage_hdrm_mv, rc; + int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; + int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip; + + /* RESISTANCE = esr_uohm + rslow_uohm */ + rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE, + &rbatt_uohm); + if (rc < 0) { + dev_err(&led->pdev->dev, "bms psy does not support resistance, rc=%d\n", + rc); + return rc; + } + + /* If no battery is connected, return max possible flash current */ + if (!rbatt_uohm) + return FLASH_LED_MAX_TOTAL_CURRENT_MA; + + rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv); + if (rc < 0) { + dev_err(&led->pdev->dev, "bms psy does not support OCV, rc=%d\n", + rc); + return rc; + } + + rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW, + &ibat_now); + if (rc < 0) { + dev_err(&led->pdev->dev, "bms psy does not support current, rc=%d\n", + rc); + return rc; + } + + rbatt_uohm += led->pdata->rpara_uohm; + voltage_hdrm_mv = qpnp_flash_led_get_voltage_headroom(led); + vph_flash_vdip = + VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold) + + FLASH_VDIP_MARGIN; + + /* Check if LMH_MITIGATION needs to be triggered */ + if (!led->trigger_lmh && (ocv_uv < led->pdata->lmh_ocv_threshold_uv || + rbatt_uohm > led->pdata->lmh_rbatt_threshold_uohm)) { + led->trigger_lmh = true; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SW(led->base), + FLASH_LED_LMH_MITIGATION_EN_MASK, + FLASH_LED_LMH_MITIGATION_ENABLE); + if (rc < 0) { + dev_err(&led->pdev->dev, "trigger lmh mitigation failed, rc=%d\n", + rc); + return rc; + } + + /* Wait for LMH mitigation to take effect */ + udelay(100); + + return qpnp_flash_led_calc_max_current(led); + } + + /* + * Calculate the maximum current that can pulled out of the battery + * before the battery voltage dips below a safe threshold. + */ + ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV, + rbatt_uohm); + + if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) { + /* + * If the calculated current is below the OCP threshold, then + * use it as the possible flash current. + */ + ibat_flash_ua = ibat_safe_ua - ibat_now; + vph_flash_uv = vph_flash_vdip; + } else { + /* + * If the calculated current is above the OCP threshold, then + * use the ocp threshold instead. + * + * Any higher current will be tripping the battery OCP. + */ + ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now; + vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm + * led->pdata->ibatt_ocp_threshold_ua, UCONV); + } + /* Calculate the input voltage of the flash module. */ + vin_flash_uv = max((led->pdata->vled_max_uv + + (voltage_hdrm_mv * MCONV)), VIN_FLASH_MIN_UV); + /* Calculate the available power for the flash module. */ + avail_flash_power_fw = BOB_EFFICIENCY * vph_flash_uv * ibat_flash_ua; + /* + * Calculate the available amount of current the flash module can draw + * before collapsing the battery. (available power/ flash input voltage) + */ + avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV); + dev_dbg(&led->pdev->dev, "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d\n", + avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm, + led->trigger_lmh); + return min(FLASH_LED_MAX_TOTAL_CURRENT_MA, + (int)(avail_flash_ua / MCONV)); +} + +static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led) +{ + int thermal_current_lim = 0; + int rc; + u8 thermal_thrsh1, thermal_thrsh2, thermal_thrsh3, otst_status; + + /* Store THERMAL_THRSHx register values */ + rc = qpnp_flash_led_masked_read(led, + FLASH_LED_REG_THERMAL_THRSH1(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + &thermal_thrsh1); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_read(led, + FLASH_LED_REG_THERMAL_THRSH2(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + &thermal_thrsh2); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_read(led, + FLASH_LED_REG_THERMAL_THRSH3(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + &thermal_thrsh3); + if (rc < 0) + return rc; + + /* Lower THERMAL_THRSHx thresholds to minimum */ + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH1(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + FLASH_LED_THERMAL_THRSH_MIN); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH2(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + FLASH_LED_THERMAL_THRSH_MIN); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH3(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + FLASH_LED_THERMAL_THRSH_MIN); + if (rc < 0) + return rc; + + /* Check THERMAL_OTST status */ + rc = qpnp_flash_led_read(led, + FLASH_LED_REG_LED_STATUS2(led->base), + &otst_status); + if (rc < 0) + return rc; + + /* Look up current limit based on THERMAL_OTST status */ + if (otst_status) + thermal_current_lim = + led->pdata->thermal_derate_current[otst_status >> 1]; + + /* Restore THERMAL_THRESHx registers to original values */ + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH1(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + thermal_thrsh1); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH2(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + thermal_thrsh2); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_THERMAL_THRSH3(led->base), + FLASH_LED_THERMAL_THRSH_MASK, + thermal_thrsh3); + if (rc < 0) + return rc; + + return thermal_current_lim; +} + +static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led) { - return 3750; + int max_avail_current, thermal_current_lim = 0; + + led->trigger_lmh = false; + max_avail_current = qpnp_flash_led_calc_max_current(led); + if (led->pdata->thermal_derate_en) + thermal_current_lim = + qpnp_flash_led_calc_thermal_current_lim(led); + + if (thermal_current_lim) + max_avail_current = min(max_avail_current, thermal_current_lim); + + return max_avail_current; } static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) @@ -397,6 +727,18 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) if (rc < 0) return rc; + if (led->trigger_lmh) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SW(led->base), + FLASH_LED_LMH_MITIGATION_EN_MASK, + FLASH_LED_LMH_MITIGATION_DISABLE); + if (rc < 0) { + dev_err(&led->pdev->dev, "disable lmh mitigation failed, rc=%d\n", + rc); + return rc; + } + } + led->enable--; if (led->enable == 0) { rc = qpnp_flash_led_masked_write(led, @@ -442,7 +784,6 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) } } - qpnp_flash_led_regulator_enable(led, snode, false); snode->enabled = false; return 0; } @@ -464,10 +805,6 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) return rc; } - rc = qpnp_flash_led_regulator_enable(led, snode, true); - if (rc < 0) - return rc; - /* Iterate over all leds for this switch node */ val = 0; for (i = 0; i < led->num_fnodes; i++) @@ -543,6 +880,18 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) } led->enable++; + if (led->trigger_lmh) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MITIGATION_SW(led->base), + FLASH_LED_LMH_MITIGATION_EN_MASK, + FLASH_LED_LMH_MITIGATION_ENABLE); + if (rc < 0) { + dev_err(&led->pdev->dev, "trigger lmh mitigation failed, rc=%d\n", + rc); + return rc; + } + } + rc = qpnp_flash_led_masked_write(led, FLASH_LED_EN_LED_CTRL(led->base), snode->led_mask, val); @@ -553,12 +902,13 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) return 0; } -int qpnp_flash_led_prepare(struct led_trigger *trig, int options) +int qpnp_flash_led_prepare(struct led_trigger *trig, int options, + int *max_current) { struct led_classdev *led_cdev = trigger_to_lcdev(trig); struct flash_switch_data *snode; struct qpnp_flash_led *led; - int rc, val = 0; + int rc; if (!led_cdev) { pr_err("Invalid led_trigger provided\n"); @@ -568,7 +918,7 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options) snode = container_of(led_cdev, struct flash_switch_data, cdev); led = dev_get_drvdata(&snode->pdev->dev); - if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { + if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) { dev_err(&led->pdev->dev, "Invalid options %d\n", options); return -EINVAL; } @@ -582,16 +932,26 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options) } } + if (options & DISABLE_REGULATOR) { + rc = qpnp_flash_led_regulator_enable(led, snode, false); + if (rc < 0) { + dev_err(&led->pdev->dev, + "disable regulator failed, rc=%d\n", rc); + return rc; + } + } + if (options & QUERY_MAX_CURRENT) { - val = qpnp_flash_led_get_max_avail_current(snode, led); - if (val < 0) { + rc = qpnp_flash_led_get_max_avail_current(led); + if (rc < 0) { dev_err(&led->pdev->dev, - "query max current failed, rc=%d\n", val); - return val; + "query max current failed, rc=%d\n", rc); + return rc; } + *max_current = rc; } - return val; + return 0; } static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, @@ -1143,6 +1503,28 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, led->pdata->current_derate_en_cfg = (vph_droop_det << 2) | (open_circuit_det << 1) | short_circuit_det; + led->pdata->thermal_derate_en = + of_property_read_bool(node, "qcom,thermal-derate-en"); + + if (led->pdata->thermal_derate_en) { + led->pdata->thermal_derate_current = + devm_kcalloc(&led->pdev->dev, + FLASH_LED_THERMAL_OTST_LEVELS, + sizeof(int), GFP_KERNEL); + if (!led->pdata->thermal_derate_current) + return -ENOMEM; + + rc = of_property_read_u32_array(node, + "qcom,thermal-derate-current", + led->pdata->thermal_derate_current, + FLASH_LED_THERMAL_OTST_LEVELS); + if (rc < 0) { + dev_err(&led->pdev->dev, "Unable to read thermal current limits, rc=%d\n", + rc); + return rc; + } + } + led->pdata->vph_droop_debounce = FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT; rc = of_property_read_u32(node, "qcom,vph-droop-debounce-us", &val); if (!rc) { @@ -1204,6 +1586,84 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } + led->pdata->vled_max_uv = FLASH_LED_VLED_MAX_DEFAULT_UV; + rc = of_property_read_u32(node, "qcom,vled-max-uv", &val); + if (!rc) { + led->pdata->vled_max_uv = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse vled_max voltage, rc=%d\n", + rc); + return rc; + } + + led->pdata->ibatt_ocp_threshold_ua = + FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA; + rc = of_property_read_u32(node, "qcom,ibatt-ocp-threshold-ua", &val); + if (!rc) { + led->pdata->ibatt_ocp_threshold_ua = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse ibatt_ocp threshold, rc=%d\n", + rc); + return rc; + } + + led->pdata->rpara_uohm = FLASH_LED_RPARA_DEFAULT_UOHM; + rc = of_property_read_u32(node, "qcom,rparasitic-uohm", &val); + if (!rc) { + led->pdata->rpara_uohm = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse rparasitic, rc=%d\n", + rc); + return rc; + } + + led->pdata->lmh_ocv_threshold_uv = + FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV; + rc = of_property_read_u32(node, "qcom,lmh-ocv-threshold-uv", &val); + if (!rc) { + led->pdata->lmh_ocv_threshold_uv = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse lmh ocv threshold, rc=%d\n", + rc); + return rc; + } + + led->pdata->lmh_rbatt_threshold_uohm = + FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM; + rc = of_property_read_u32(node, "qcom,lmh-rbatt-threshold-uohm", &val); + if (!rc) { + led->pdata->lmh_rbatt_threshold_uohm = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse lmh rbatt threshold, rc=%d\n", + rc); + return rc; + } + + led->pdata->lmh_level = FLASH_LED_LMH_LEVEL_DEFAULT; + rc = of_property_read_u32(node, "qcom,lmh-level", &val); + if (!rc) { + led->pdata->lmh_level = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse lmh_level, rc=%d\n", + rc); + return rc; + } + + led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT; + rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val); + if (!rc) { + led->pdata->lmh_mitigation_sel = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to parse lmh_mitigation_sel, rc=%d\n", + rc); + return rc; + } + + if (led->pdata->lmh_mitigation_sel > FLASH_LED_LMH_MITIGATION_SEL_MAX) { + dev_err(&led->pdev->dev, "Invalid lmh_mitigation_sel specified\n"); + return -EINVAL; + } + led->pdata->all_ramp_up_done_irq = of_irq_get_byname(node, "all-ramp-up-done-irq"); if (led->pdata->all_ramp_up_done_irq < 0) diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c index 3e19cf6796a3..95b4c42a5adb 100644 --- a/drivers/leds/leds-qpnp-flash.c +++ b/drivers/leds/leds-qpnp-flash.c @@ -82,6 +82,7 @@ #define FLASH_LED_HDRM_SNS_ENABLE_MASK 0x81 #define FLASH_MASK_MODULE_CONTRL_MASK 0xE0 #define FLASH_FOLLOW_OTST2_RB_MASK 0x08 +#define FLASH_PREPARE_OPTIONS_MASK 0x08 #define FLASH_LED_TRIGGER_DEFAULT "none" #define FLASH_LED_HEADROOM_DEFAULT_MV 500 @@ -1155,12 +1156,13 @@ error_regulator_enable: return rc; } -int qpnp_flash_led_prepare(struct led_trigger *trig, int options) +int qpnp_flash_led_prepare(struct led_trigger *trig, int options, + int *max_current) { struct led_classdev *led_cdev = trigger_to_lcdev(trig); struct flash_node_data *flash_node; struct qpnp_flash_led *led; - int rc, val = 0; + int rc; if (!led_cdev) { pr_err("Invalid led_trigger provided\n"); @@ -1170,7 +1172,7 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options) flash_node = container_of(led_cdev, struct flash_node_data, cdev); led = dev_get_drvdata(&flash_node->pdev->dev); - if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { + if (!(options & FLASH_PREPARE_OPTIONS_MASK)) { dev_err(&led->pdev->dev, "Invalid options %d\n", options); return -EINVAL; } @@ -1184,16 +1186,26 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options) } } + if (options & DISABLE_REGULATOR) { + rc = flash_regulator_enable(led, flash_node, false); + if (rc < 0) { + dev_err(&led->pdev->dev, + "disable regulator failed, rc=%d\n", rc); + return rc; + } + } + if (options & QUERY_MAX_CURRENT) { - val = qpnp_flash_led_get_max_avail_current(flash_node, led); - if (val < 0) { + rc = qpnp_flash_led_get_max_avail_current(flash_node, led); + if (rc < 0) { dev_err(&led->pdev->dev, - "query max current failed, rc=%d\n", val); - return val; + "query max current failed, rc=%d\n", rc); + return rc; } + *max_current = rc; } - return val; + return 0; } static void qpnp_flash_led_work(struct work_struct *work) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 8721fc18eaa8..ac2d508269a4 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -184,6 +184,7 @@ int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data, case V4L2_PIX_FMT_P16RGGB10: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_META: + case V4L2_PIX_FMT_META10: case V4L2_PIX_FMT_GREY: stream_info->num_planes = 1; stream_info->format_factor = ISP_Q2; @@ -288,6 +289,7 @@ static uint32_t msm_isp_axi_get_plane_size( case V4L2_PIX_FMT_QGBRG10: case V4L2_PIX_FMT_QGRBG10: case V4L2_PIX_FMT_QRGGB10: + case V4L2_PIX_FMT_META10: /* TODO: fix me */ size = plane_cfg[plane_idx].output_height * plane_cfg[plane_idx].output_width; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 5e24b146619d..e47a8de30aa9 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -1396,6 +1396,7 @@ int msm_isp_cal_word_per_line(uint32_t output_format, case V4L2_PIX_FMT_SGBRG10DPCM8: case V4L2_PIX_FMT_SGRBG10DPCM8: case V4L2_PIX_FMT_SRGGB10DPCM8: + case V4L2_PIX_FMT_META10: val = CAL_WORD(pixel_per_line, 5, 32); break; case V4L2_PIX_FMT_SBGGR12: @@ -1581,6 +1582,8 @@ int msm_isp_get_bit_per_pixel(uint32_t output_format) case V4L2_PIX_FMT_P16GBRG10: case V4L2_PIX_FMT_P16GRBG10: case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_META10: + case MSM_V4L2_PIX_FMT_META10: return 10; case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SGBRG12: diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 1fc51d900cf8..d0ce1de1162a 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -920,6 +920,14 @@ static int32_t msm_flash_get_dt_data(struct device_node *of_node, fctrl->flash_driver_type = FLASH_DRIVER_I2C; } + /* Read the flash and torch source info from device tree node */ + rc = msm_flash_get_pmic_source_info(of_node, fctrl); + if (rc < 0) { + pr_err("%s:%d msm_flash_get_pmic_source_info failed rc %d\n", + __func__, __LINE__, rc); + return rc; + } + /* Read the gpio information from device tree */ rc = msm_sensor_driver_get_gpio_data( &(fctrl->power_info.gpio_conf), of_node); @@ -934,13 +942,6 @@ static int32_t msm_flash_get_dt_data(struct device_node *of_node, CDBG("%s:%d fctrl->flash_driver_type = %d", __func__, __LINE__, fctrl->flash_driver_type); - /* Read the flash and torch source info from device tree node */ - rc = msm_flash_get_pmic_source_info(of_node, fctrl); - if (rc < 0) { - pr_err("%s:%d msm_flash_get_pmic_source_info failed rc %d\n", - __func__, __LINE__, rc); - return rc; - } return rc; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index 9ce02d21704a..dae1b51bfaa8 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -35,10 +35,14 @@ static int sde_rotator_stat_show(struct seq_file *s, void *data) u64 count = stats->count; int num_events; s64 proc_max, proc_min, proc_avg; + s64 swoh_max, swoh_min, swoh_avg; proc_max = 0; proc_min = S64_MAX; proc_avg = 0; + swoh_max = 0; + swoh_min = S64_MAX; + swoh_avg = 0; if (count > SDE_ROTATOR_NUM_EVENTS) { num_events = SDE_ROTATOR_NUM_EVENTS; @@ -59,9 +63,12 @@ static int sde_rotator_stat_show(struct seq_file *s, void *data) s64 proc_time = ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_RETIRE], start_time)); + s64 sw_overhead_time = + ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FLUSH], + start_time)); seq_printf(s, - "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld d:%lld sdq:%lld ddq:%lld t:%lld\n", + "s:%d sq:%lld dq:%lld fe:%lld q:%lld c:%lld fl:%lld d:%lld sdq:%lld ddq:%lld t:%lld oht:%lld\n", i, ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_FENCE], ts[SDE_ROTATOR_TS_SRCQB])), @@ -81,21 +88,30 @@ static int sde_rotator_stat_show(struct seq_file *s, void *data) ts[SDE_ROTATOR_TS_RETIRE])), ktime_to_us(ktime_sub(ts[SDE_ROTATOR_TS_DSTDQB], ts[SDE_ROTATOR_TS_RETIRE])), - proc_time); + proc_time, sw_overhead_time); proc_max = max(proc_max, proc_time); proc_min = min(proc_min, proc_time); proc_avg += proc_time; + + swoh_max = max(swoh_max, sw_overhead_time); + swoh_min = min(swoh_min, sw_overhead_time); + swoh_avg += sw_overhead_time; } proc_avg = (num_events) ? DIV_ROUND_CLOSEST_ULL(proc_avg, num_events) : 0; + swoh_avg = (num_events) ? + DIV_ROUND_CLOSEST_ULL(swoh_avg, num_events) : 0; seq_printf(s, "count:%llu\n", count); seq_printf(s, "fai1:%llu\n", stats->fail_count); seq_printf(s, "t_max:%lld\n", proc_max); seq_printf(s, "t_min:%lld\n", proc_min); seq_printf(s, "t_avg:%lld\n", proc_avg); + seq_printf(s, "swoh_max:%lld\n", swoh_max); + seq_printf(s, "swoh_min:%lld\n", swoh_min); + seq_printf(s, "swoh_avg:%lld\n", swoh_avg); return 0; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h index 8a1da4e6d632..fd247d10128c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h @@ -33,7 +33,7 @@ #define SDE_ROTATOR_DRV_NAME "sde_rotator" /* Event logging constants */ -#define SDE_ROTATOR_NUM_EVENTS 256 +#define SDE_ROTATOR_NUM_EVENTS 4096 #define SDE_ROTATOR_NUM_TIMESTAMPS SDE_ROTATOR_TS_MAX struct sde_rotator_device; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index c8d14a6d253b..8d5e3b649271 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -24,6 +24,7 @@ #include <linux/dma-mapping.h> #include <linux/dma-buf.h> #include <linux/msm_ion.h> +#include <linux/clk/msm-clk.h> #include "sde_rotator_core.h" #include "sde_rotator_util.h" @@ -1963,6 +1964,12 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr) if (ret) goto error_hw_rev_init; + /* set rotator CBCR to shutoff memory/periphery on clock off.*/ + clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk, + CLKFLAG_NORETAIN_MEM); + clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk, + CLKFLAG_NORETAIN_PERIPH); + return 0; error_hw_rev_init: if (rot->irq_num >= 0) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 96fefea39241..ee3df23115a5 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -601,10 +601,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls) -static int set_buffer_size(struct msm_vidc_inst *inst, - u32 buffer_size, enum hal_buffer buffer_type); -static int update_output_buffer_size(struct msm_vidc_inst *inst, - struct v4l2_format *f, int num_planes); static int vdec_hal_to_v4l2(int id, int value); static u32 get_frame_size_nv12(int plane, @@ -1181,13 +1177,6 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) for (i = 0; i < fmt->num_planes; ++i) inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; - rc = update_output_buffer_size(inst, f, fmt->num_planes); - if (rc) { - dprintk(VIDC_ERR, - "%s - failed to update buffer size: %d\n", - __func__, rc); - goto exit; - } } if (stride && scanlines) { @@ -1220,95 +1209,6 @@ exit: return rc; } -static int set_buffer_size(struct msm_vidc_inst *inst, - u32 buffer_size, enum hal_buffer buffer_type) -{ - int rc = 0; - struct hfi_device *hdev; - struct hal_buffer_size_minimum b; - - if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); - return -EINVAL; - } - - hdev = inst->core->device; - - dprintk(VIDC_DBG, - "Set minimum buffer size = %d for buffer type %d to fw\n", - buffer_size, buffer_type); - - b.buffer_type = buffer_type; - b.buffer_size = buffer_size; - rc = call_hfi_op(hdev, session_set_property, - inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, - &b); - if (rc) - dprintk(VIDC_ERR, - "%s - failed to set actual buffer size %u on firmware\n", - __func__, buffer_size); - return rc; -} - -static int update_output_buffer_size(struct msm_vidc_inst *inst, - struct v4l2_format *f, int num_planes) -{ - int rc = 0, i = 0; - struct hal_buffer_requirements *bufreq; - - if (!inst || !f) - return -EINVAL; - - /* - * Firmware expects driver to always set the minimum buffer - * size negotiated with the v4l2 client. Firmware will use this - * size to check for buffer sufficiency in dynamic buffer mode. - */ - for (i = 0; i < num_planes; ++i) { - enum hal_buffer type = msm_comm_get_hal_output_buffer(inst); - - if (EXTRADATA_IDX(num_planes) && - i == EXTRADATA_IDX(num_planes)) - continue; - - bufreq = get_buff_req_buffer(inst, type); - if (!bufreq) - goto exit; - - if (f->fmt.pix_mp.plane_fmt[i].sizeimage >= - bufreq->buffer_size) { - rc = set_buffer_size(inst, - f->fmt.pix_mp.plane_fmt[i].sizeimage, type); - if (rc) - goto exit; - } - } - - /* - * Set min buffer size for DPB buffers as well. - * Firmware mandates setting of minimum buffer size - * and actual buffer count for both OUTPUT and OUTPUT2. - * Hence we are setting back the same buffer size - * information back to firmware. - */ - if (msm_comm_get_stream_output_mode(inst) == - HAL_VIDEO_DECODER_SECONDARY) { - bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); - if (!bufreq) { - rc = -EINVAL; - goto exit; - } - - rc = set_buffer_size(inst, bufreq->buffer_size, - HAL_BUFFER_OUTPUT); - if (rc) - goto exit; - } - -exit: - return rc; -} - static int set_default_properties(struct msm_vidc_inst *inst) { struct hfi_device *hdev; @@ -1370,13 +1270,9 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width; inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height; - if (msm_comm_get_stream_output_mode(inst) == - HAL_VIDEO_DECODER_PRIMARY) { - inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; - inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; - msm_comm_set_color_format(inst, HAL_BUFFER_OUTPUT, + msm_comm_set_color_format(inst, + msm_comm_get_hal_output_buffer(inst), f->fmt.pix_mp.pixelformat); - } inst->fmts[fmt->type] = fmt; if (msm_comm_get_stream_output_mode(inst) == @@ -1384,8 +1280,6 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; frame_sz.width = inst->prop.width[CAPTURE_PORT]; frame_sz.height = inst->prop.height[CAPTURE_PORT]; - msm_comm_set_color_format(inst, HAL_BUFFER_OUTPUT2, - f->fmt.pix_mp.pixelformat); dprintk(VIDC_DBG, "buffer type = %d width = %d, height = %d\n", frame_sz.buffer_type, frame_sz.width, @@ -1793,12 +1687,28 @@ static inline int start_streaming(struct msm_vidc_inst *inst) int rc = 0; struct hfi_device *hdev; bool slave_side_cp = inst->core->resources.slave_side_cp; + struct hal_buffer_size_minimum b; + unsigned int buffer_size; + struct msm_vidc_format *fmt = NULL; + fmt = inst->fmts[CAPTURE_PORT]; + buffer_size = fmt->get_frame_size(0, + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); hdev = inst->core->device; if (msm_comm_get_stream_output_mode(inst) == - HAL_VIDEO_DECODER_SECONDARY) + HAL_VIDEO_DECODER_SECONDARY) { rc = msm_vidc_check_scaling_supported(inst); + b.buffer_type = HAL_BUFFER_OUTPUT2; + } else { + b.buffer_type = HAL_BUFFER_OUTPUT; + } + + b.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); if (rc) { dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); return -EINVAL; diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index c08084a54e86..55ee7e02973c 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -2702,7 +2702,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) (inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_HEVC); if (is_cont_intra_supported) { - if (air_mbs || air_ref || cir_mbs) + if (ctrl->val != HAL_INTRA_REFRESH_NONE) enable.enable = true; else enable.enable = false; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 1f071ba36ec1..587aa930cf9c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3010,6 +3010,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst, struct hal_buffer_requirements *output_buf, *extradata_buf; int i; struct hfi_device *hdev; + struct hal_buffer_size_minimum b; hdev = inst->core->device; @@ -3026,6 +3027,11 @@ static int set_output_buffers(struct msm_vidc_inst *inst, output_buf->buffer_size); buffer_size = output_buf->buffer_size; + b.buffer_type = buffer_type; + b.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); if (extradata_buf) { diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c index 1b93c83ae98e..0c5754341991 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/drivers/mfd/wcd9xxx-irq.c @@ -741,6 +741,7 @@ static int wcd9xxx_irq_remove(struct platform_device *pdev) wmb(); irq_domain_remove(data->domain); kfree(data); + domain->host_data = NULL; return 0; } diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c index c63e8d6d926e..4bce44008c45 100644 --- a/drivers/mfd/wcd9xxx-slimslave.c +++ b/drivers/mfd/wcd9xxx-slimslave.c @@ -220,7 +220,8 @@ int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, __func__, ch_cnt, rate, WATER_MARK_VAL); /* slim_define_ch api */ prop.prot = SLIM_AUTO_ISO; - if (rate == 44100) { + if ((rate == 44100) || (rate == 88200) || (rate == 176400) || + (rate == 352800)) { prop.baser = SLIM_RATE_11025HZ; prop.ratem = (rate/11025); } else { diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 011e7412dcc0..f10c47dcbde5 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -101,7 +101,7 @@ static void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp) mask_halp ? WIL6210_IRQ_DISABLE : WIL6210_IRQ_DISABLE_NO_HALP); } -static void wil6210_mask_halp(struct wil6210_priv *wil) +void wil6210_mask_halp(struct wil6210_priv *wil) { wil_dbg_irq(wil, "%s()\n", __func__); @@ -503,6 +503,13 @@ static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) offsetof(struct RGF_ICR, ICR)); u32 imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, IMV)); + + /* HALP interrupt can be unmasked when misc interrupts are + * masked + */ + if (icr_misc & BIT_DMA_EP_MISC_ICR_HALP) + return 0; + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index bd8f481da44c..49bd9b45ce22 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1119,13 +1119,16 @@ void wil_halp_vote(struct wil6210_priv *wil) if (++wil->halp.ref_cnt == 1) { wil6210_set_halp(wil); rc = wait_for_completion_timeout(&wil->halp.comp, to_jiffies); - if (!rc) + if (!rc) { wil_err(wil, "%s: HALP vote timed out\n", __func__); - else + /* Mask HALP as done in case the interrupt is raised */ + wil6210_mask_halp(wil); + } else { wil_dbg_misc(wil, "%s: HALP vote completed after %d ms\n", __func__, jiffies_to_msecs(to_jiffies - rc)); + } } wil_dbg_misc(wil, "%s: end, HALP ref_cnt (%d)\n", __func__, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index cd0bfdf34248..16e8ba570011 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -831,6 +831,7 @@ void wil_unmask_irq(struct wil6210_priv *wil); void wil_configure_interrupt_moderation(struct wil6210_priv *wil); void wil_disable_irq(struct wil6210_priv *wil); void wil_enable_irq(struct wil6210_priv *wil); +void wil6210_mask_halp(struct wil6210_priv *wil); /* P2P */ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request); diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c index 9b962a63c3d8..154310020997 100644 --- a/drivers/nfc/nq-nci.c +++ b/drivers/nfc/nq-nci.c @@ -63,6 +63,8 @@ struct nqx_dev { bool nfc_ven_enabled; /* NFC_IRQ state */ bool irq_enabled; + /* NFC_IRQ wake-up state */ + bool irq_wake_up; spinlock_t irq_enabled_lock; unsigned int count_irq; /* Initial CORE RESET notification */ @@ -372,6 +374,9 @@ int nfc_ioctl_power_states(struct file *filp, unsigned long arg) } else { dev_dbg(&nqx_dev->client->dev, "keeping en_gpio high\n"); } + } else { + dev_dbg(&nqx_dev->client->dev, "ese_gpio invalid, set en_gpio to low\n"); + gpio_set_value(nqx_dev->en_gpio, 0); } r = nqx_clock_deselect(nqx_dev); if (r < 0) @@ -777,21 +782,29 @@ static int nqx_probe(struct i2c_client *client, r = gpio_request(platform_data->ese_gpio, "nfc-ese_pwr"); if (r) { + nqx_dev->ese_gpio = -EINVAL; dev_err(&client->dev, "%s: unable to request nfc ese gpio [%d]\n", __func__, platform_data->ese_gpio); /* ese gpio optional so we should continue */ } else { nqx_dev->ese_gpio = platform_data->ese_gpio; - } - r = gpio_direction_output(platform_data->ese_gpio, 0); - if (r) { - dev_err(&client->dev, - "%s: cannot set direction for nfc ese gpio [%d]\n", - __func__, platform_data->ese_gpio); - /* ese gpio optional so we should continue */ + r = gpio_direction_output(platform_data->ese_gpio, 0); + if (r) { + /* + * free ese gpio and set invalid + * to avoid further use + */ + gpio_free(platform_data->ese_gpio); + nqx_dev->ese_gpio = -EINVAL; + dev_err(&client->dev, + "%s: cannot set direction for nfc ese gpio [%d]\n", + __func__, platform_data->ese_gpio); + /* ese gpio optional so we should continue */ + } } } else { + nqx_dev->ese_gpio = -EINVAL; dev_err(&client->dev, "%s: ese gpio not provided\n", __func__); /* ese gpio optional so we should continue */ @@ -821,7 +834,6 @@ static int nqx_probe(struct i2c_client *client, nqx_dev->en_gpio = platform_data->en_gpio; nqx_dev->irq_gpio = platform_data->irq_gpio; nqx_dev->firm_gpio = platform_data->firm_gpio; - nqx_dev->ese_gpio = platform_data->ese_gpio; nqx_dev->clkreq_gpio = platform_data->clkreq_gpio; nqx_dev->pdata = platform_data; @@ -889,6 +901,7 @@ static int nqx_probe(struct i2c_client *client, device_init_wakeup(&client->dev, true); device_set_wakeup_capable(&client->dev, true); i2c_set_clientdata(client, nqx_dev); + nqx_dev->irq_wake_up = false; dev_err(&client->dev, "%s: probing NFCC NQxxx exited successfully\n", @@ -909,7 +922,7 @@ err_clkreq_gpio: gpio_free(platform_data->clkreq_gpio); err_ese_gpio: /* optional gpio, not sure was configured in probe */ - if (nqx_dev->ese_gpio) + if (nqx_dev->ese_gpio > 0) gpio_free(platform_data->ese_gpio); err_firm_gpio: gpio_free(platform_data->firm_gpio); @@ -950,7 +963,7 @@ static int nqx_remove(struct i2c_client *client) mutex_destroy(&nqx_dev->read_mutex); gpio_free(nqx_dev->clkreq_gpio); /* optional gpio, not sure was configured in probe */ - if (nqx_dev->ese_gpio) + if (nqx_dev->ese_gpio > 0) gpio_free(nqx_dev->ese_gpio); gpio_free(nqx_dev->firm_gpio); gpio_free(nqx_dev->irq_gpio); @@ -969,17 +982,22 @@ static int nqx_suspend(struct device *device) struct i2c_client *client = to_i2c_client(device); struct nqx_dev *nqx_dev = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev) && nqx_dev->irq_enabled) - enable_irq_wake(client->irq); + if (device_may_wakeup(&client->dev) && nqx_dev->irq_enabled) { + if (!enable_irq_wake(client->irq)) + nqx_dev->irq_wake_up = true; + } return 0; } static int nqx_resume(struct device *device) { struct i2c_client *client = to_i2c_client(device); + struct nqx_dev *nqx_dev = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + if (device_may_wakeup(&client->dev) && nqx_dev->irq_wake_up) { + if (!disable_irq_wake(client->irq)) + nqx_dev->irq_wake_up = false; + } return 0; } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 84932513f008..0acebc87ec20 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -796,14 +796,13 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) #endif /* CONFIG_BLK_DEV_INITRD */ #ifdef CONFIG_SERIAL_EARLYCON -extern struct of_device_id __earlycon_of_table[]; static int __init early_init_dt_scan_chosen_serial(void) { int offset; const char *p; int l; - const struct of_device_id *match = __earlycon_of_table; + const struct earlycon_id *match; const void *fdt = initial_boot_params; offset = fdt_path_offset(fdt, "/chosen"); @@ -826,19 +825,20 @@ static int __init early_init_dt_scan_chosen_serial(void) if (offset < 0) return -ENODEV; - while (match->compatible[0]) { + for (match = __earlycon_table; match < __earlycon_table_end; match++) { u64 addr; - if (fdt_node_check_compatible(fdt, offset, match->compatible)) { - match++; + if (!match->compatible[0]) + continue; + + if (fdt_node_check_compatible(fdt, offset, match->compatible)) continue; - } addr = fdt_translate_address(fdt, offset); if (addr == OF_BAD_ADDR) return -ENXIO; - of_setup_earlycon(addr, match->data); + of_setup_earlycon(addr, match->setup); return 0; } return -ENODEV; diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 53e3af9052a9..71154ec99d3c 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -550,6 +550,7 @@ static void armpmu_init(struct arm_pmu *armpmu) .stop = armpmu_stop, .read = armpmu_read, .filter_match = armpmu_filter_match, + .events_across_hotplug = 1, }; } @@ -686,31 +687,12 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) return 0; } -/* - * PMU hardware loses all context when a CPU goes offline. - * When a CPU is hotplugged back in, since some hardware registers are - * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading - * junk values out of them. - */ -static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, - void *hcpu) -{ - int cpu = (unsigned long)hcpu; - struct arm_pmu *pmu = container_of(b, struct arm_pmu, hotplug_nb); - - if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) - return NOTIFY_DONE; - - if (!cpumask_test_cpu(cpu, &pmu->supported_cpus)) - return NOTIFY_DONE; - - if (pmu->reset) - pmu->reset(pmu); - else - return NOTIFY_DONE; - - return NOTIFY_OK; -} +struct cpu_pm_pmu_args { + struct arm_pmu *armpmu; + unsigned long cmd; + int cpu; + int ret; +}; #ifdef CONFIG_CPU_PM static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd) @@ -758,15 +740,19 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd) } } -static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, - void *v) +static void cpu_pm_pmu_common(void *info) { - struct arm_pmu *armpmu = container_of(b, struct arm_pmu, cpu_pm_nb); + struct cpu_pm_pmu_args *data = info; + struct arm_pmu *armpmu = data->armpmu; + unsigned long cmd = data->cmd; + int cpu = data->cpu; struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events); - if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus)) - return NOTIFY_DONE; + if (!cpumask_test_cpu(cpu, &armpmu->supported_cpus)) { + data->ret = NOTIFY_DONE; + return; + } /* * Always reset the PMU registers on power-up even if @@ -775,8 +761,12 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, if (cmd == CPU_PM_EXIT && armpmu->reset) armpmu->reset(armpmu); - if (!enabled) - return NOTIFY_OK; + if (!enabled) { + data->ret = NOTIFY_OK; + return; + } + + data->ret = NOTIFY_OK; switch (cmd) { case CPU_PM_ENTER: @@ -784,15 +774,29 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, cpu_pm_pmu_setup(armpmu, cmd); break; case CPU_PM_EXIT: - cpu_pm_pmu_setup(armpmu, cmd); case CPU_PM_ENTER_FAILED: + cpu_pm_pmu_setup(armpmu, cmd); armpmu->start(armpmu); break; default: - return NOTIFY_DONE; + data->ret = NOTIFY_DONE; + break; } - return NOTIFY_OK; + return; +} + +static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, + void *v) +{ + struct cpu_pm_pmu_args data = { + .armpmu = container_of(b, struct arm_pmu, cpu_pm_nb), + .cmd = cmd, + .cpu = smp_processor_id(), + }; + + cpu_pm_pmu_common(&data); + return data.ret; } static int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu) @@ -808,8 +812,62 @@ static void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu) #else static inline int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu) { return 0; } static inline void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu) { } +static inline void cpu_pm_pmu_common(void *info) { } #endif +/* + * PMU hardware loses all context when a CPU goes offline. + * When a CPU is hotplugged back in, since some hardware registers are + * UNKNOWN at reset, the PMU must be explicitly reset to avoid reading + * junk values out of them. + */ +static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, + void *hcpu) +{ + unsigned long masked_action = (action & ~CPU_TASKS_FROZEN); + struct cpu_pm_pmu_args data = { + .armpmu = container_of(b, struct arm_pmu, hotplug_nb), + .cpu = (unsigned long)hcpu, + }; + + if (!cpumask_test_cpu(data.cpu, &data.armpmu->supported_cpus)) + return NOTIFY_DONE; + + switch (masked_action) { + case CPU_STARTING: + data.cmd = CPU_PM_EXIT; + break; + case CPU_DYING: + data.cmd = CPU_PM_ENTER; + break; + case CPU_DOWN_FAILED: + data.cmd = CPU_PM_ENTER_FAILED; + break; + case CPU_ONLINE: + if (data.armpmu->plat_device) { + struct platform_device *pmu_device = + data.armpmu->plat_device; + int irq = platform_get_irq(pmu_device, 0); + + if (irq >= 0 && irq_is_percpu(irq)) { + smp_call_function_single(data.cpu, + cpu_pmu_enable_percpu_irq, &irq, 1); + } + } + return NOTIFY_DONE; + default: + return NOTIFY_DONE; + } + + if (smp_processor_id() == data.cpu) + cpu_pm_pmu_common(&data); + else + smp_call_function_single(data.cpu, + cpu_pm_pmu_common, &data, 1); + + return data.ret; +} + static int cpu_pmu_init(struct arm_pmu *cpu_pmu) { int err; diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index d2e31c3b0945..af1e5a70d585 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -643,6 +643,13 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl) GSIERR("failed to register isr for %u\n", props->irq); return -GSI_STATUS_ERROR; } + + res = enable_irq_wake(props->irq); + if (res) + GSIERR("failed to enable wake irq %u\n", props->irq); + else + GSIERR("GSI irq is wake enabled %u\n", props->irq); + } else { GSIERR("do not support interrupt type %u\n", props->intr); return -GSI_STATUS_UNSUPPORTED_OP; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index fc3d9f355da6..a2f1c5c9af27 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -3613,6 +3613,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, ipa_ctx->ipa_bam_remote_mode = resource_p->ipa_bam_remote_mode; ipa_ctx->modem_cfg_emb_pipe_flt = resource_p->modem_cfg_emb_pipe_flt; ipa_ctx->wan_rx_ring_size = resource_p->wan_rx_ring_size; + ipa_ctx->lan_rx_ring_size = resource_p->lan_rx_ring_size; ipa_ctx->skip_uc_pipe_reset = resource_p->skip_uc_pipe_reset; ipa_ctx->use_dma_zone = resource_p->use_dma_zone; ipa_ctx->tethered_flow_control = resource_p->tethered_flow_control; @@ -4150,6 +4151,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_bam_remote_mode = false; ipa_drv_res->modem_cfg_emb_pipe_flt = false; ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; + ipa_drv_res->lan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; smmu_info.disable_htw = of_property_read_bool(pdev->dev.of_node, "qcom,smmu-disable-htw"); @@ -4172,16 +4174,27 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, IPADBG(": found ipa_drv_res->ipa_hw_mode = %d", ipa_drv_res->ipa_hw_mode); - /* Get IPA WAN RX pool sizee */ + /* Get IPA WAN / LAN RX pool sizes */ result = of_property_read_u32(pdev->dev.of_node, "qcom,wan-rx-ring-size", &ipa_drv_res->wan_rx_ring_size); if (result) - IPADBG("using default for wan-rx-ring-size\n"); + IPADBG("using default for wan-rx-ring-size = %u\n", + ipa_drv_res->wan_rx_ring_size); else IPADBG(": found ipa_drv_res->wan-rx-ring-size = %u", ipa_drv_res->wan_rx_ring_size); + result = of_property_read_u32(pdev->dev.of_node, + "qcom,lan-rx-ring-size", + &ipa_drv_res->lan_rx_ring_size); + if (result) + IPADBG("using default for lan-rx-ring-size = %u\n", + ipa_drv_res->lan_rx_ring_size); + else + IPADBG(": found ipa_drv_res->lan-rx-ring-size = %u", + ipa_drv_res->lan_rx_ring_size); + ipa_drv_res->use_ipa_teth_bridge = of_property_read_bool(pdev->dev.of_node, "qcom,use-ipa-tethering-bridge"); @@ -4440,7 +4453,7 @@ static int ipa_smmu_uc_cb_probe(struct device *dev) cb->dev = dev; cb->mapping = arm_iommu_create_mapping(msm_iommu_get_bus(dev), cb->va_start, cb->va_size); - if (IS_ERR(cb->mapping)) { + if (IS_ERR_OR_NULL(cb->mapping)) { IPADBG("Fail to create mapping\n"); /* assume this failure is because iommu driver is not ready */ return -EPROBE_DEFER; @@ -4543,7 +4556,7 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) cb->mapping = arm_iommu_create_mapping(msm_iommu_get_bus(dev), cb->va_start, cb->va_size); - if (IS_ERR(cb->mapping)) { + if (IS_ERR_OR_NULL(cb->mapping)) { IPADBG("Fail to create mapping\n"); /* assume this failure is because iommu driver is not ready */ return -EPROBE_DEFER; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 005508fdcdc1..c0ac544fa271 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -3142,7 +3142,7 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in, ipa_replenish_rx_cache; } sys->rx_pool_sz = - IPA_GENERIC_RX_POOL_SZ; + ipa_ctx->lan_rx_ring_size; in->ipa_ep_cfg.aggr.aggr_byte_limit = IPA_GENERIC_AGGR_BYTE_LIMIT; in->ipa_ep_cfg.aggr.aggr_pkt_limit = diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index 5ea7a08b3135..5b8abb25cfb0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -1102,6 +1102,7 @@ struct ipa_context { struct ipa_uc_wdi_ctx uc_wdi_ctx; struct ipa_uc_ntn_ctx uc_ntn_ctx; u32 wan_rx_ring_size; + u32 lan_rx_ring_size; bool skip_uc_pipe_reset; bool smmu_present; bool smmu_s1_bypass; @@ -1171,6 +1172,7 @@ struct ipa_plat_drv_res { bool ipa_bam_remote_mode; bool modem_cfg_emb_pipe_flt; u32 wan_rx_ring_size; + u32 lan_rx_ring_size; bool skip_uc_pipe_reset; bool use_dma_zone; bool tethered_flow_control; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 4db07bad7d93..9115e30b2b21 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -3953,6 +3953,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, ipa3_ctx->ipa_wdi2 = resource_p->ipa_wdi2; ipa3_ctx->use_64_bit_dma_mask = resource_p->use_64_bit_dma_mask; ipa3_ctx->wan_rx_ring_size = resource_p->wan_rx_ring_size; + ipa3_ctx->lan_rx_ring_size = resource_p->lan_rx_ring_size; ipa3_ctx->skip_uc_pipe_reset = resource_p->skip_uc_pipe_reset; ipa3_ctx->tethered_flow_control = resource_p->tethered_flow_control; ipa3_ctx->transport_prototype = resource_p->transport_prototype; @@ -4432,6 +4433,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_wdi2 = false; ipa_drv_res->use_64_bit_dma_mask = false; ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; + ipa_drv_res->lan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; ipa_drv_res->apply_rg10_wa = false; ipa_drv_res->gsi_ch20_wa = false; @@ -4461,16 +4463,27 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, IPADBG(": found ipa_drv_res->ipa3_hw_mode = %d", ipa_drv_res->ipa3_hw_mode); - /* Get IPA WAN RX pool size */ + /* Get IPA WAN / LAN RX pool size */ result = of_property_read_u32(pdev->dev.of_node, "qcom,wan-rx-ring-size", &ipa_drv_res->wan_rx_ring_size); if (result) - IPADBG("using default for wan-rx-ring-size\n"); + IPADBG("using default for wan-rx-ring-size = %u\n", + ipa_drv_res->wan_rx_ring_size); else IPADBG(": found ipa_drv_res->wan-rx-ring-size = %u", ipa_drv_res->wan_rx_ring_size); + result = of_property_read_u32(pdev->dev.of_node, + "qcom,lan-rx-ring-size", + &ipa_drv_res->lan_rx_ring_size); + if (result) + IPADBG("using default for lan-rx-ring-size = %u\n", + ipa_drv_res->lan_rx_ring_size); + else + IPADBG(": found ipa_drv_res->lan-rx-ring-size = %u", + ipa_drv_res->lan_rx_ring_size); + ipa_drv_res->use_ipa_teth_bridge = of_property_read_bool(pdev->dev.of_node, "qcom,use-ipa-tethering-bridge"); @@ -4752,7 +4765,7 @@ static int ipa_smmu_uc_cb_probe(struct device *dev) cb->dev = dev; cb->mapping = arm_iommu_create_mapping(msm_iommu_get_bus(dev), cb->va_start, cb->va_size); - if (IS_ERR(cb->mapping)) { + if (IS_ERR_OR_NULL(cb->mapping)) { IPADBG("Fail to create mapping\n"); /* assume this failure is because iommu driver is not ready */ return -EPROBE_DEFER; @@ -4866,7 +4879,7 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) cb->dev = dev; cb->mapping = arm_iommu_create_mapping(msm_iommu_get_bus(dev), cb->va_start, cb->va_size); - if (IS_ERR(cb->mapping)) { + if (IS_ERR_OR_NULL(cb->mapping)) { IPADBG("Fail to create mapping\n"); /* assume this failure is because iommu driver is not ready */ return -EPROBE_DEFER; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 4c600c6131e9..8f61827b50b4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -3171,7 +3171,7 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in, sys->free_rx_wrapper = ipa3_recycle_rx_wrapper; sys->rx_pool_sz = - IPA_GENERIC_RX_POOL_SZ; + ipa3_ctx->lan_rx_ring_size; in->ipa_ep_cfg.aggr.aggr_byte_limit = IPA_GENERIC_AGGR_BYTE_LIMIT; in->ipa_ep_cfg.aggr.aggr_pkt_limit = diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index a7587b2b9675..f9018e3f47bf 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1202,6 +1202,7 @@ struct ipa3_context { struct ipa3_uc_wdi_ctx uc_wdi_ctx; struct ipa3_uc_ntn_ctx uc_ntn_ctx; u32 wan_rx_ring_size; + u32 lan_rx_ring_size; bool skip_uc_pipe_reset; enum ipa_transport_type transport_prototype; unsigned long gsi_dev_hdl; @@ -1259,6 +1260,7 @@ struct ipa3_plat_drv_res { bool ipa_wdi2; bool use_64_bit_dma_mask; u32 wan_rx_ring_size; + u32 lan_rx_ring_size; bool skip_uc_pipe_reset; enum ipa_transport_type transport_prototype; bool apply_rg10_wa; diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c index e9b0945f0782..eab0e47e7cd2 100644 --- a/drivers/platform/msm/sps/sps.c +++ b/drivers/platform/msm/sps/sps.c @@ -289,7 +289,8 @@ static ssize_t sps_set_bam_addr(struct file *file, const char __user *buf, } else { vir_addr = &bam->base; num_pipes = bam->props.num_pipes; - bam->ipc_loglevel = log_level_sel; + if (log_level_sel <= SPS_IPC_MAX_LOGLEVEL) + bam->ipc_loglevel = log_level_sel; } switch (reg_dump_option) { @@ -500,7 +501,7 @@ static void sps_debugfs_init(void) debugfs_buf_size = 0; debugfs_buf_used = 0; wraparound = false; - log_level_sel = 0; + log_level_sel = SPS_IPC_MAX_LOGLEVEL + 1; dent = debugfs_create_dir("sps", 0); if (IS_ERR(dent)) { @@ -2207,6 +2208,8 @@ int sps_register_bam_device(const struct sps_bam_props *bam_props, if (bam_props->ipc_loglevel) bam->ipc_loglevel = bam_props->ipc_loglevel; + else + bam->ipc_loglevel = SPS_IPC_DEFAULT_LOGLEVEL; ok = sps_bam_device_init(bam); mutex_unlock(&bam->lock); diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h index e09d3cda1d31..1b4ca69bee16 100644 --- a/drivers/platform/msm/sps/spsi.h +++ b/drivers/platform/msm/sps/spsi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2016, 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 @@ -52,6 +52,8 @@ #define MAX_MSG_LEN 80 #define SPS_IPC_LOGPAGES 10 #define SPS_IPC_REG_DUMP_FACTOR 3 +#define SPS_IPC_DEFAULT_LOGLEVEL 3 +#define SPS_IPC_MAX_LOGLEVEL 4 /* Connection mapping control struct */ struct sps_rm { diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 78c5c47e4e8b..9e400a9eee5c 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -5241,30 +5241,44 @@ static int cpr3_debug_closed_loop_enable_set(void *data, u64 val) ctrl->cpr_allowed_sw = enable; - rc = cpr3_regulator_update_ctrl_state(ctrl); - if (rc) { - cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n", - enable, rc); - goto done; - } - - if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) { - rc = cpr3_clock_enable(ctrl); + if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) { + cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, + ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop + ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE + : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); + } else { + rc = cpr3_regulator_update_ctrl_state(ctrl); if (rc) { - cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc); + cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n", + enable, rc); goto done; } - ctrl->cpr_enabled = true; - cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, - CPR3_PD_THROTTLE_DISABLE); + if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) { + rc = cpr3_clock_enable(ctrl); + if (rc) { + cpr3_err(ctrl, "clock enable failed, rc=%d\n", + rc); + goto done; + } + ctrl->cpr_enabled = true; - cpr3_clock_disable(ctrl); - ctrl->cpr_enabled = false; - } + cpr3_write(ctrl, CPR3_REG_PD_THROTTLE, + CPR3_PD_THROTTLE_DISABLE); - cpr3_debug(ctrl, "closed-loop=%s\n", enable ? "enabled" : "disabled"); + cpr3_clock_disable(ctrl); + ctrl->cpr_enabled = false; + } + } + if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { + cpr3_debug(ctrl, "closed-loop=%s\n", enable ? + "enabled" : "disabled"); + } else { + cpr3_debug(ctrl, "closed-loop=%s\n", enable && + ctrl->use_hw_closed_loop ? "enabled" : "disabled"); + } done: mutex_unlock(&ctrl->lock); return 0; @@ -5298,7 +5312,8 @@ DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops, * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the * value of the CPR controller use_hw_closed_loop flag which * switches between software closed-loop and hardware closed-loop - * operation + * operation for CPR3 and CPR4 controllers and between open-loop + * and full hardware closed-loop operation for CPRh controllers. * @data: Pointer to private data which is equal to the CPR * controller pointer * @val: New value for use_hw_closed_loop @@ -5327,7 +5342,8 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) } } - cpr3_ctrl_loop_disable(ctrl); + if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) + cpr3_ctrl_loop_disable(ctrl); ctrl->use_hw_closed_loop = use_hw_closed_loop; @@ -5343,7 +5359,7 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) ctrl->cpr_enabled = true; } - if (ctrl->use_hw_closed_loop) + if (ctrl->use_hw_closed_loop && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0); if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) { @@ -5352,6 +5368,12 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) ctrl->use_hw_closed_loop ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); + } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) { + cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, + ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop + ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE + : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE); } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP, ctrl->use_hw_closed_loop @@ -5365,7 +5387,7 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) ctrl->cpr_enabled = false; } - if (ctrl->use_hw_closed_loop && ctrl->ctrl_type != CPR_CTRL_TYPE_CPR4) { + if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { rc = regulator_enable(ctrl->vdd_limit_regulator); if (rc) { cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n", @@ -5379,7 +5401,7 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) goto done; } } else if (!ctrl->use_hw_closed_loop - && ctrl->ctrl_type != CPR_CTRL_TYPE_CPR4) { + && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) { rc = regulator_disable(ctrl->vdd_limit_regulator); if (rc) { cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n", @@ -5395,35 +5417,42 @@ static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val) } } - /* - * Due to APM and mem-acc floor restriction constraints, the closed-loop - * voltage may be different when using software closed-loop vs hardware - * closed-loop. Therefore, reset the cached closed-loop voltage for all - * corners to the corresponding open-loop voltage when switching between - * SW and HW closed-loop mode. - */ - for (i = 0; i < ctrl->thread_count; i++) { - for (j = 0; j < ctrl->thread[i].vreg_count; j++) { - vreg = &ctrl->thread[i].vreg[j]; - for (k = 0; k < vreg->corner_count; k++) - vreg->corner[k].last_volt + if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) { + /* + * Due to APM and mem-acc floor restriction constraints, + * the closed-loop voltage may be different when using + * software closed-loop vs hardware closed-loop. Therefore, + * reset the cached closed-loop voltage for all corners to the + * corresponding open-loop voltage when switching between + * SW and HW closed-loop mode. + */ + for (i = 0; i < ctrl->thread_count; i++) { + for (j = 0; j < ctrl->thread[i].vreg_count; j++) { + vreg = &ctrl->thread[i].vreg[j]; + for (k = 0; k < vreg->corner_count; k++) + vreg->corner[k].last_volt = vreg->corner[k].open_loop_volt; + } } - } - - /* Skip last_volt caching */ - ctrl->last_corner_was_closed_loop = false; - rc = cpr3_regulator_update_ctrl_state(ctrl); - if (rc) { - cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n", - use_hw_closed_loop, rc); - goto done; - } + /* Skip last_volt caching */ + ctrl->last_corner_was_closed_loop = false; - cpr3_debug(ctrl, "closed-loop mode=%s\n", - use_hw_closed_loop ? "HW" : "SW"); + rc = cpr3_regulator_update_ctrl_state(ctrl); + if (rc) { + cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n", + use_hw_closed_loop, rc); + goto done; + } + cpr3_debug(ctrl, "CPR mode=%s\n", + use_hw_closed_loop ? + "HW closed-loop" : "SW closed-loop"); + } else { + cpr3_debug(ctrl, "CPR mode=%s\n", + ctrl->cpr_allowed_sw && use_hw_closed_loop ? + "full HW closed-loop" : "open-loop"); + } done: mutex_unlock(&ctrl->lock); return 0; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 4d406c51d884..ab602d2ef392 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1283,7 +1283,14 @@ static void ufshcd_gate_work(struct work_struct *work) unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); - if (hba->clk_gating.is_suspended) { + /* + * In case you are here to cancel this work the gating state + * would be marked as REQ_CLKS_ON. In this case save time by + * skipping the gating work and exit after changing the clock + * state to CLKS_ON. + */ + if (hba->clk_gating.is_suspended || + (hba->clk_gating.state == REQ_CLKS_ON)) { hba->clk_gating.state = CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -7977,6 +7984,13 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) hba->clk_gating.is_suspended = true; hba->hibern8_on_idle.is_suspended = true; + /* + * Disable auto hibern8 to prevent unnecessary hibern8 enter/exit + * during suspend path + */ + if (ufshcd_is_auto_hibern8_supported(hba)) + ufshcd_set_auto_hibern8_timer(hba, 0); + if (hba->clk_scaling.is_allowed) { cancel_work_sync(&hba->clk_scaling.suspend_work); cancel_work_sync(&hba->clk_scaling.resume_work); @@ -8084,6 +8098,10 @@ enable_gating: ufshcd_resume_clkscaling(hba); hba->hibern8_on_idle.is_suspended = false; hba->clk_gating.is_suspended = false; + /* Re-enable auto hibern8 in case of suspend failure */ + if (ufshcd_is_auto_hibern8_supported(hba)) + ufshcd_set_auto_hibern8_timer(hba, + hba->hibern8_on_idle.delay_ms); ufshcd_release_all(hba); out: hba->pm_op_in_progress = 0; @@ -8177,6 +8195,13 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); + /* + * Enable auto hibern8 after successful resume to prevent + * unnecessary hibern8 enter/exit during resume path + */ + if (ufshcd_is_auto_hibern8_supported(hba)) + ufshcd_set_auto_hibern8_timer(hba, + hba->hibern8_on_idle.delay_ms); /* Schedule clock gating in case of no access to UFS device yet */ ufshcd_release_all(hba); goto out; diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c index 1ca5109615fe..0509b46948cb 100644 --- a/drivers/slimbus/slim-msm.c +++ b/drivers/slimbus/slim-msm.c @@ -392,13 +392,13 @@ void msm_dealloc_port(struct slim_controller *ctrl, u8 pn) if (pn >= dev->port_nums) return; endpoint = &dev->pipes[pn]; - if (dev->pipes[pn].connected) - msm_slim_disconn_pipe_port(dev, pn); - if (endpoint->sps) { + if (dev->pipes[pn].connected) { struct sps_connect *config = &endpoint->config; - msm_slim_free_endpoint(endpoint); + msm_slim_disconn_pipe_port(dev, pn); msm_slim_sps_mem_free(dev, &config->desc); } + if (endpoint->sps) + msm_slim_free_endpoint(endpoint); } enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr, diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c45cbfa8a786..ecfa6954f1e6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -669,7 +669,7 @@ config MSM_EVENT_TIMER config MSM_AVTIMER tristate "Avtimer Driver" - depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 + depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 || MSM_QDSP6_APRV2_GLINK help This driver gets the Q6 out of power collapsed state and exposes ioctl control to read avtimer tick. @@ -761,4 +761,13 @@ config QSEE_IPC_IRQ_BRIDGE interrupt from a remote subsystem directed towards Qualcomm Technologies, Inc. Secure Execution Environment(QSEE). +config WCD_DSP_GLINK + tristate "WCD DSP GLINK Driver" + depends on MSM_GLINK + default y if SND_SOC_WCD934X=y + help + This option enables driver which provides communication interface + between MSM and WCD DSP over glink transport protocol. This driver + provides read and write interface via char device. + source "drivers/soc/qcom/memshare/Kconfig" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 269b72c68b68..913f5d55e53b 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -97,3 +97,4 @@ obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o +obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 16ca3eaee61b..bfe2072ee554 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -33,6 +33,7 @@ #include <linux/dma-mapping.h> #include <linux/qmi_encdec.h> #include <linux/ipc_logging.h> +#include <linux/msm-bus.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/icnss.h> #include <soc/qcom/msm_qmi_interface.h> @@ -43,7 +44,6 @@ #define ICNSS_PANIC 1 #define WLFW_TIMEOUT_MS 3000 #define WLFW_SERVICE_INS_ID_V01 0 -#define SMMU_CLOCK_NAME "smmu_aggre2_noc_clk" #define MAX_PROP_SIZE 32 #define MAX_VOLTAGE_LEVEL 2 #define VREG_ON 1 @@ -203,7 +203,8 @@ static struct icnss_data { size_t smmu_iova_len; dma_addr_t smmu_iova_ipa_start; size_t smmu_iova_ipa_len; - struct clk *smmu_clk; + struct msm_bus_scale_pdata *bus_scale_table; + uint32_t bus_client; struct qmi_handle *wlfw_clnt; struct list_head event_list; spinlock_t event_lock; @@ -1789,44 +1790,60 @@ int icnss_smmu_map(struct device *dev, } EXPORT_SYMBOL(icnss_smmu_map); -static struct clk *icnss_clock_init(struct device *dev, const char *cname) +static int icnss_bw_vote(struct icnss_data *priv, int index) { - struct clk *c; - long rate; - - if (of_property_match_string(dev->of_node, "clock-names", cname) < 0) { - icnss_pr_err("Clock %s not found!", cname); - return NULL; - } - - c = devm_clk_get(dev, cname); - if (IS_ERR(c)) { - icnss_pr_err("Couldn't get clock %s!", cname); - return NULL; - } + int ret = 0; - if (clk_get_rate(c) == 0) { - rate = clk_round_rate(c, 1000); - clk_set_rate(c, rate); - } + icnss_pr_dbg("Vote %d for msm_bus, state 0x%lx\n", + index, priv->state); + ret = msm_bus_scale_client_update_request(priv->bus_client, index); + if (ret) + icnss_pr_err("Fail to vote %d: ret %d, state 0x%lx!\n", + index, ret, priv->state); - return c; + return ret; } -static int icnss_clock_enable(struct clk *c) +static int icnss_bw_init(struct icnss_data *priv) { int ret = 0; - ret = clk_prepare_enable(c); + priv->bus_scale_table = msm_bus_cl_get_pdata(priv->pdev); + if (!priv->bus_scale_table) { + icnss_pr_err("Missing entry for msm_bus scale table\n"); + return -EINVAL; + } - if (ret < 0) - icnss_pr_err("Couldn't enable clock: %d!\n", ret); + priv->bus_client = msm_bus_scale_register_client(priv->bus_scale_table); + if (!priv->bus_client) { + icnss_pr_err("Fail to register with bus_scale client\n"); + ret = -EINVAL; + goto out; + } + + ret = icnss_bw_vote(priv, 1); + if (ret) + goto out; + + return 0; + +out: + msm_bus_cl_clear_pdata(priv->bus_scale_table); return ret; } -static void icnss_clock_disable(struct clk *c) +static void icnss_bw_deinit(struct icnss_data *priv) { - clk_disable_unprepare(c); + if (!priv) + return; + + if (priv->bus_client) { + icnss_bw_vote(priv, 0); + msm_bus_scale_unregister_client(priv->bus_client); + } + + if (priv->bus_scale_table) + msm_bus_cl_clear_pdata(priv->bus_scale_table); } static int icnss_smmu_init(struct device *dev) @@ -2460,12 +2477,9 @@ static int icnss_probe(struct platform_device *pdev) goto err_smmu_init; } - penv->smmu_clk = icnss_clock_init(&pdev->dev, SMMU_CLOCK_NAME); - if (penv->smmu_clk) { - ret = icnss_clock_enable(penv->smmu_clk); - if (ret < 0) - goto err_smmu_clock_enable; - } + ret = icnss_bw_init(penv); + if (ret) + goto err_bw_init; } penv->skip_qmi = of_property_read_bool(dev->of_node, @@ -2478,7 +2492,7 @@ static int icnss_probe(struct platform_device *pdev) if (!penv->event_wq) { icnss_pr_err("Workqueue creation failed\n"); ret = -EFAULT; - goto err_smmu_clock_enable; + goto err_alloc_workqueue; } INIT_WORK(&penv->event_work, icnss_driver_event_work); @@ -2503,7 +2517,9 @@ static int icnss_probe(struct platform_device *pdev) err_qmi: if (penv->event_wq) destroy_workqueue(penv->event_wq); -err_smmu_clock_enable: +err_alloc_workqueue: + icnss_bw_deinit(penv); +err_bw_init: if (penv->smmu_mapping) icnss_smmu_remove(&pdev->dev); err_smmu_init: @@ -2538,11 +2554,7 @@ static int icnss_remove(struct platform_device *pdev) if (penv->event_wq) destroy_workqueue(penv->event_wq); - if (penv->smmu_mapping) { - if (penv->smmu_clk) - icnss_clock_disable(penv->smmu_clk); - icnss_smmu_remove(&pdev->dev); - } + icnss_bw_deinit(penv); if (penv->msa_va) dma_free_coherent(&pdev->dev, penv->msa_mem_size, diff --git a/drivers/soc/qcom/irq-helper.c b/drivers/soc/qcom/irq-helper.c index 2bb71464d165..7bb371f7991e 100644 --- a/drivers/soc/qcom/irq-helper.c +++ b/drivers/soc/qcom/irq-helper.c @@ -72,7 +72,7 @@ static ssize_t show_deploy(struct kobject *kobj, struct attribute *attr, { struct irq_helper *irq = to_irq_helper(kobj); - return snprintf(buf, sizeof(irq->deploy), "%u\n", irq->deploy); + return snprintf(buf, PAGE_SIZE, "%u\n", irq->deploy); } IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL); diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 9426e0751c4a..24018c544b06 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -208,8 +208,8 @@ static int servreg_loc_send_msg(struct msg_desc *req_desc, static int service_locator_send_msg(struct pd_qmi_client_data *pd) { struct msg_desc req_desc, resp_desc; - struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp; - struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req; + struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp = NULL; + struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req = NULL; int rc; int db_rev_count = 0, domains_read = 0; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 45af4b601634..30eff96fc27d 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -1920,10 +1920,20 @@ static int spcom_device_release(struct inode *inode, struct file *filp) pr_debug("Close file [%s].\n", name); + if (strcmp(name, DEVICE_NAME) == 0) { + pr_debug("root dir skipped.\n"); + return 0; + } + + if (strcmp(name, "sp_ssr") == 0) { + pr_debug("sp_ssr dev node skipped.\n"); + return 0; + } + ch = filp->private_data; if (!ch) { - pr_err("ch is NULL, file name %s.\n", file_to_filename(filp)); + pr_debug("ch is NULL, file name %s.\n", file_to_filename(filp)); return -ENODEV; } diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c new file mode 100644 index 000000000000..310903b10a98 --- /dev/null +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -0,0 +1,948 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/cdev.h> +#include <linux/platform_device.h> +#include <soc/qcom/glink.h> +#include "sound/wcd-dsp-glink.h" + +#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink" +#define WDSP_MAX_WRITE_SIZE (512 * 1024) +#define WDSP_MAX_READ_SIZE (4 * 1024) + +#define MINOR_NUMBER_COUNT 1 +#define WDSP_EDGE "wdsp" +#define RESP_QUEUE_SIZE 3 +#define QOS_PKT_SIZE 1024 + +struct wdsp_glink_dev { + struct class *cls; + struct device *dev; + struct cdev cdev; + dev_t dev_num; +}; + +struct wdsp_glink_rsp_que { + /* Size of valid data in buffer */ + u32 buf_size; + + /* Response buffer */ + u8 buf[WDSP_MAX_READ_SIZE]; +}; + +struct wdsp_glink_tx_buf { + struct work_struct tx_work; + + /* Glink channel information */ + struct wdsp_glink_ch *ch; + + /* Tx buffer to send to glink */ + u8 buf[0]; +}; + +struct wdsp_glink_ch { + struct wdsp_glink_priv *wpriv; + + /* Glink channel handle */ + void *handle; + + /* Channel states like connect, disconnect */ + int channel_state; + struct mutex mutex; + + /* To free up the channel memory */ + bool free_mem; + + /* Glink channel configuration */ + struct wdsp_glink_ch_cfg ch_cfg; +}; + +struct wdsp_glink_state { + /* Glink link state information */ + enum glink_link_state link_state; + void *handle; +}; + +struct wdsp_glink_priv { + /* Respone buffer related */ + u8 rsp_cnt; + struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE]; + struct completion rsp_complete; + struct mutex rsp_mutex; + + /* Glink channel related */ + struct wdsp_glink_state glink_state; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + struct work_struct ch_open_cls_wrk; + struct workqueue_struct *work_queue; + + struct device *dev; +}; + +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch); +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch); + +/* + * wdsp_glink_notify_rx - Glink notify rx callback for responses + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Rx data + * size: Size of the Rx data + */ +static void wdsp_glink_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, + size_t size) +{ + u8 *rx_buf; + u8 rsp_cnt; + struct wdsp_glink_ch *ch; + struct wdsp_glink_priv *wpriv; + + if (!ptr || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + rx_buf = (u8 *)ptr; + if (size > WDSP_MAX_READ_SIZE) { + dev_err(wpriv->dev, "%s: Size %zd is greater than allowed %d\n", + __func__, size, WDSP_MAX_READ_SIZE); + size = WDSP_MAX_READ_SIZE; + } + + mutex_lock(&wpriv->rsp_mutex); + rsp_cnt = wpriv->rsp_cnt; + if (rsp_cnt >= RESP_QUEUE_SIZE) { + dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__); + rsp_cnt = 0; + } + dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt); + + memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size); + wpriv->rsp[rsp_cnt].buf_size = size; + wpriv->rsp_cnt = ++rsp_cnt; + mutex_unlock(&wpriv->rsp_mutex); + + complete(&wpriv->rsp_complete); + glink_rx_done(handle, ptr, true); +} + +/* + * wdsp_glink_notify_tx_done - Glink notify tx done callback to + * free tx buffer + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Tx data + */ +static void wdsp_glink_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + if (!pkt_priv) { + pr_err("%s: Invalid parameter\n", __func__); + return; + } + /* Free tx pkt */ + kfree(pkt_priv); +} + +/* + * wdsp_glink_notify_rx_intent_req - Glink notify rx intent request callback + * to queue buffer to receive from remote client + * handle: Opaque channel handle returned by GLink + * priv: Private pointer to the channel + * req_size: Size of intent to be queued + */ +static bool wdsp_glink_notify_rx_intent_req(void *handle, const void *priv, + size_t req_size) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int rc = 0; + bool ret = false; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + goto done; + } + if (req_size > WDSP_MAX_READ_SIZE) { + pr_err("%s: Invalid req_size %zd\n", __func__, req_size); + goto done; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + dev_dbg(wpriv->dev, "%s: intent size %zd requested for ch name %s", + __func__, req_size, ch->ch_cfg.name); + + mutex_lock(&ch->mutex); + rc = glink_queue_rx_intent(ch->handle, ch, req_size); + if (IS_ERR_VALUE(ret)) { + dev_err(wpriv->dev, "%s: Failed to queue rx intent\n", + __func__); + mutex_unlock(&ch->mutex); + goto done; + } + mutex_unlock(&ch->mutex); + ret = true; + +done: + return ret; +} + +/* + * wdsp_glink_notify_state - Glink channel state information event callback + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * event: channel state event + */ +static void wdsp_glink_notify_state(void *handle, const void *priv, + unsigned event) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int i, ret = 0; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + mutex_lock(&ch->mutex); + ch->channel_state = event; + if (event == GLINK_CONNECTED) { + dev_dbg(wpriv->dev, "%s: glink channel: %s connected\n", + __func__, ch->ch_cfg.name); + + for (i = 0; i < ch->ch_cfg.no_of_intents; i++) { + dev_dbg(wpriv->dev, "%s: intent_size = %d\n", __func__, + ch->ch_cfg.intents_size[i]); + ret = glink_queue_rx_intent(ch->handle, ch, + ch->ch_cfg.intents_size[i]); + if (IS_ERR_VALUE(ret)) + dev_warn(wpriv->dev, "%s: Failed to queue intent %d of size %d\n", + __func__, i, + ch->ch_cfg.intents_size[i]); + } + + ret = glink_qos_latency(ch->handle, ch->ch_cfg.latency_in_us, + QOS_PKT_SIZE); + if (IS_ERR_VALUE(ret)) + dev_warn(wpriv->dev, "%s: Failed to request qos %d for ch %s\n", + __func__, ch->ch_cfg.latency_in_us, + ch->ch_cfg.name); + + mutex_unlock(&ch->mutex); + } else if (event == GLINK_LOCAL_DISCONNECTED) { + /* + * Don't use dev_dbg here as dev may not be valid if channel + * closed from driver close. + */ + pr_debug("%s: channel: %s disconnected locally\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + + if (ch->free_mem) { + kfree(ch); + ch = NULL; + } + } else if (event == GLINK_REMOTE_DISCONNECTED) { + dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + /* + * If remote disconnect happens, local side also has + * to close the channel and reopen again as per glink + */ + if (!wdsp_glink_close_ch(ch)) + wdsp_glink_open_ch(ch); + } +} + +/* + * wdsp_glink_close_ch - Internal function to close glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + int ret = 0; + + + mutex_lock(&ch->mutex); + + dev_dbg(wpriv->dev, "%s: ch %s closing\n", __func__, ch->ch_cfg.name); + ret = glink_close(ch->handle); + if (IS_ERR_VALUE(ret)) + dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n", + __func__, ret); + + mutex_unlock(&ch->mutex); + + return ret; +} + +/* + * wdsp_glink_open_ch - Internal function to open glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + struct glink_open_config open_cfg; + int ret = 0; + + memset(&open_cfg, 0, sizeof(open_cfg)); + open_cfg.options = GLINK_OPT_INITIAL_XPORT; + open_cfg.edge = WDSP_EDGE; + open_cfg.notify_rx = wdsp_glink_notify_rx; + open_cfg.notify_tx_done = wdsp_glink_notify_tx_done; + open_cfg.notify_state = wdsp_glink_notify_state; + open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req; + open_cfg.priv = ch; + open_cfg.name = ch->ch_cfg.name; + + dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n", + __func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us, + ch->ch_cfg.no_of_intents); + + mutex_lock(&ch->mutex); + ch->handle = glink_open(&open_cfg); + if (IS_ERR_OR_NULL(ch->handle)) { + dev_err(wpriv->dev, "%s: glink_open failed %s\n", + __func__, ch->ch_cfg.name); + ch->handle = NULL; + ret = -EINVAL; + } + mutex_unlock(&ch->mutex); + + return ret; +} + +/* + * wdsp_glink_close_all_ch - Internal function to close all glink channels + * wpriv: Wdsp_glink private structure + */ +static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv) +{ + int i; + + for (i = 0; i < wpriv->no_of_channels; i++) + if (wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[i]); +} + +/* + * wdsp_glink_open_all_ch - Internal function to open all glink channels + * wpriv: Wdsp_glink private structure + */ +static int wdsp_glink_open_all_ch(struct wdsp_glink_priv *wpriv) +{ + int ret = 0, i, j; + + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i]) { + ret = wdsp_glink_open_ch(wpriv->ch[i]); + if (IS_ERR_VALUE(ret)) + goto err_open; + } + } + goto done; + +err_open: + for (j = 0; j < i; j++) + if (wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[j]); + +done: + return ret; +} + +/* + * wdsp_glink_ch_open_wq - Work function to open glink channels + * work: Work structure + */ +static void wdsp_glink_ch_open_cls_wrk(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = container_of(work, struct wdsp_glink_priv, + ch_open_cls_wrk); + + if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_DOWN\n", + __func__); + + wdsp_glink_close_all_ch(wpriv); + } else if (wpriv->glink_state.link_state == GLINK_LINK_STATE_UP) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_UP\n", + __func__); + + wdsp_glink_open_all_ch(wpriv); + } +} + +/* + * wdsp_glink_link_state_cb - Glink link state callback to inform + * about link states + * cb_info: Glink link state callback information structure + * priv: Private structure of link state passed while register + */ +static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + struct wdsp_glink_priv *wpriv; + + if (!cb_info || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + wpriv = (struct wdsp_glink_priv *)priv; + wpriv->glink_state.link_state = cb_info->link_state; + queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk); +} + +/* + * wdsp_glink_ch_info_init- Internal function to allocate channel memory + * and register with glink + * wpriv: Wdsp_glink private structure. + * pkt: Glink registration packet contains glink channel information. + */ +static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, + struct wdsp_reg_pkt *pkt) +{ + int ret = 0, i, j; + struct glink_link_info link_info; + struct wdsp_glink_ch_cfg *ch_cfg; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + u8 *payload; + u32 ch_size, ch_cfg_size; + + payload = (u8 *)pkt->payload; + no_of_channels = pkt->no_of_channels; + + ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *), + GFP_KERNEL); + if (!ch) { + ret = -ENOMEM; + goto done; + } + + for (i = 0; i < no_of_channels; i++) { + ch_cfg = (struct wdsp_glink_ch_cfg *)payload; + ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) + + (sizeof(u32) * ch_cfg->no_of_intents); + ch_size = sizeof(struct wdsp_glink_ch) + + (sizeof(u32) * ch_cfg->no_of_intents); + + dev_dbg(wpriv->dev, "%s: channels = %d, ch_cfg_size %d", + __func__, no_of_channels, ch_cfg_size); + + ch[i] = kzalloc(ch_size, GFP_KERNEL); + if (!ch[i]) { + ret = -ENOMEM; + goto err_ch_mem; + } + ch[i]->channel_state = GLINK_LOCAL_DISCONNECTED; + memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size); + payload += ch_cfg_size; + + mutex_init(&ch[i]->mutex); + ch[i]->wpriv = wpriv; + } + wpriv->ch = ch; + wpriv->no_of_channels = no_of_channels; + + INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk); + + /* Register glink link_state notification */ + link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb; + link_info.transport = NULL; + link_info.edge = WDSP_EDGE; + + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; + wpriv->glink_state.handle = glink_register_link_state_cb(&link_info, + wpriv); + if (!wpriv->glink_state.handle) { + dev_err(wpriv->dev, "%s: Unable to register wdsp link state\n", + __func__); + ret = -EINVAL; + goto err_ch_mem; + } + goto done; + +err_ch_mem: + for (j = 0; j < i; j++) { + mutex_destroy(&ch[j]->mutex); + kfree(wpriv->ch[j]); + wpriv->ch[j] = NULL; + } + kfree(wpriv->ch); + wpriv->ch = NULL; + wpriv->no_of_channels = 0; + +done: + return ret; +} + +/* + * wdsp_glink_tx_buf_work - Work queue function to send tx buffer to glink + * work: Work structure + */ +static void wdsp_glink_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + int ret = 0; + + tx_buf = container_of(work, struct wdsp_glink_tx_buf, + tx_work); + ch = tx_buf->ch; + wpriv = ch->wpriv; + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n", + __func__, cpkt->ch_name, cpkt->payload_size); + + mutex_lock(&tx_buf->ch->mutex); + if (ch->channel_state == GLINK_CONNECTED) { + ret = glink_tx(ch->handle, tx_buf, + cpkt->payload, cpkt->payload_size, + GLINK_TX_REQ_INTENT); + if (IS_ERR_VALUE(ret)) + dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n", + __func__, ret); + } else { + dev_err(wpriv->dev, "%s: channel %s is not in connected state\n", + __func__, ch->ch_cfg.name); + } + mutex_unlock(&tx_buf->ch->mutex); + +} + +/* + * wdsp_glink_read - Read API to send the data to userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, ret1 = 0; + struct wdsp_glink_rsp_que *rsp; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if (count > WDSP_MAX_READ_SIZE) { + dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + __func__, count); + count = WDSP_MAX_READ_SIZE; + } + /* + * This is unblocked only from glink rx notification callback + * or from flush API. + */ + wait_for_completion(&wpriv->rsp_complete); + + mutex_lock(&wpriv->rsp_mutex); + if (wpriv->rsp_cnt) { + wpriv->rsp_cnt--; + dev_dbg(wpriv->dev, "%s: read from buffer %d\n", + __func__, wpriv->rsp_cnt); + + rsp = &wpriv->rsp[wpriv->rsp_cnt]; + if (count < rsp->buf_size) { + ret1 = copy_to_user(buf, &rsp->buf, count); + /* Return the number of bytes copied */ + ret = count; + } else { + ret1 = copy_to_user(buf, &rsp->buf, rsp->buf_size); + /* Return the number of bytes copied */ + ret = rsp->buf_size; + } + + if (ret1) { + mutex_unlock(&wpriv->rsp_mutex); + dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + } else { + /* + * This will execute only if flush API is called or + * something wrong with ref_cnt + */ + dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__, + wpriv->rsp_cnt); + ret = -EINVAL; + } + mutex_unlock(&wpriv->rsp_mutex); + +done: + return ret; +} + +/* + * wdsp_glink_write - Write API to receive the data from userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, i, tx_buf_size; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count); + + if (count > WDSP_MAX_WRITE_SIZE) { + dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_WRITE_SIZE\n", + __func__, count); + count = WDSP_MAX_WRITE_SIZE; + } + + tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf); + tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto done; + } + + ret = copy_from_user(tx_buf->buf, buf, count); + if (ret) { + dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto free_buf; + } + + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + switch (wpkt->pkt_type) { + case WDSP_REG_PKT: + ret = wdsp_glink_ch_info_init(wpriv, + (struct wdsp_reg_pkt *)wpkt->payload); + if (IS_ERR_VALUE(ret)) + dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + __func__, ret); + kfree(tx_buf); + break; + case WDSP_CMD_PKT: + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + dev_dbg(wpriv->dev, "%s: requested ch_name: %s\n", __func__, + cpkt->ch_name); + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i] && + (!strcmp(cpkt->ch_name, + wpriv->ch[i]->ch_cfg.name))) { + tx_buf->ch = wpriv->ch[i]; + break; + } + } + if (!tx_buf->ch) { + dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + __func__); + ret = -EINVAL; + goto free_buf; + } + INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->tx_work); + break; + default: + dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + ret = -EINVAL; + kfree(tx_buf); + break; + } + goto done; + +free_buf: + kfree(tx_buf); + +done: + return ret; +} + +/* + * wdsp_glink_open - Open API to initialize private data + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_dev *wdev; + + if (!inode->i_cdev) { + pr_err("%s: cdev is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + wdev = container_of(inode->i_cdev, struct wdsp_glink_dev, cdev); + + wpriv = kzalloc(sizeof(struct wdsp_glink_priv), GFP_KERNEL); + if (!wpriv) { + ret = -ENOMEM; + goto done; + } + wpriv->dev = wdev->dev; + wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq"); + if (!wpriv->work_queue) { + dev_err(wpriv->dev, "%s: Error creating wdsp_glink_wq\n", + __func__); + ret = -EINVAL; + goto err_wq; + } + + init_completion(&wpriv->rsp_complete); + mutex_init(&wpriv->rsp_mutex); + file->private_data = wpriv; + + goto done; + +err_wq: + kfree(wpriv); + +done: + return ret; +} + +/* + * wdsp_glink_flush - Flush API to unblock read. + * file: Pointer to the file structure + * id: Lock owner ID + */ +static int wdsp_glink_flush(struct file *file, fl_owner_t id) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + complete(&wpriv->rsp_complete); + + return 0; +} + +/* + * wdsp_glink_release - Release API to clean up resources. + * Whenever a file structure is shared across multiple threads, + * release won't be invoked until all copies are closed + * (file->f_count.counter should be 0). If we need to flush pending + * data when any copy is closed, you should implement the flush method. + * + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_release(struct inode *inode, struct file *file) +{ + int i, ret = 0; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + flush_workqueue(wpriv->work_queue); + /* + * Clean up glink channel memory in channel state + * callback only if close channels are called from here. + */ + if (wpriv->ch) { + for (i = 0; i < wpriv->no_of_channels; i++) + if (wpriv->ch[i]) + wpriv->ch[i]->free_mem = true; + + wdsp_glink_close_all_ch(wpriv); + kfree(wpriv->ch); + wpriv->ch = NULL; + } + + if (wpriv->glink_state.handle) + glink_unregister_link_state_cb(wpriv->glink_state.handle); + + mutex_destroy(&wpriv->rsp_mutex); + if (wpriv->work_queue) + destroy_workqueue(wpriv->work_queue); + + kfree(wpriv); + file->private_data = NULL; + +done: + return ret; +} + +static const struct file_operations wdsp_glink_fops = { + .owner = THIS_MODULE, + .open = wdsp_glink_open, + .read = wdsp_glink_read, + .write = wdsp_glink_write, + .flush = wdsp_glink_flush, + .release = wdsp_glink_release, +}; + +/* + * wdsp_glink_probe - Driver probe to expose char device + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_probe(struct platform_device *pdev) +{ + int ret; + struct wdsp_glink_dev *wdev; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) { + ret = -ENOMEM; + goto done; + } + + ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT, + WDSP_GLINK_DRIVER_NAME); + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n", + __func__, ret); + goto err_chrdev; + } + + wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->cls)) { + ret = PTR_ERR(wdev->cls); + dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n", + __func__, ret); + goto err_class; + } + + wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num, + NULL, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->dev)) { + ret = PTR_ERR(wdev->dev); + dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n", + __func__, ret); + goto err_dev_create; + } + + cdev_init(&wdev->cdev, &wdsp_glink_fops); + ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT); + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n", + __func__, ret); + goto err_cdev_add; + } + platform_set_drvdata(pdev, wdev); + goto done; + +err_cdev_add: + device_destroy(wdev->cls, wdev->dev_num); + +err_dev_create: + class_destroy(wdev->cls); + +err_class: + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + +err_chrdev: + devm_kfree(&pdev->dev, wdev); + +done: + return ret; +} + +/* + * wdsp_glink_remove - Driver remove to handle cleanup + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_remove(struct platform_device *pdev) +{ + struct wdsp_glink_dev *wdev = platform_get_drvdata(pdev); + + if (wdev) { + cdev_del(&wdev->cdev); + device_destroy(wdev->cls, wdev->dev_num); + class_destroy(wdev->cls); + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + devm_kfree(&pdev->dev, wdev); + } else { + dev_err(&pdev->dev, "%s: Invalid device data\n", __func__); + } + + return 0; +} + +static const struct of_device_id wdsp_glink_of_match[] = { + {.compatible = "qcom,wcd-dsp-glink"}, + { } +}; +MODULE_DEVICE_TABLE(of, wdsp_glink_of_match); + +static struct platform_driver wdsp_glink_driver = { + .probe = wdsp_glink_probe, + .remove = wdsp_glink_remove, + .driver = { + .name = WDSP_GLINK_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = wdsp_glink_of_match, + }, +}; + +module_platform_driver(wdsp_glink_driver); + +MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index 2cb60c11e212..266091486bf1 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -325,6 +325,7 @@ static int swrm_set_ch_map(struct swr_mstr_ctrl *swrm, void *data) GFP_KERNEL); if (!swrm->mstr_port->port) { kfree(swrm->mstr_port); + swrm->mstr_port = NULL; return -ENOMEM; } memcpy(swrm->mstr_port->port, pinfo->port, pinfo->num_port); @@ -476,7 +477,7 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, { struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); int ret = 0; - int val = 0; + int val; u8 *reg_val = (u8 *)buf; if (!swrm) { @@ -1474,7 +1475,9 @@ static int swrm_remove(struct platform_device *pdev) swrm, SWR_IRQ_FREE); if (swrm->mstr_port) { kfree(swrm->mstr_port->port); + swrm->mstr_port->port = NULL; kfree(swrm->mstr_port); + swrm->mstr_port = NULL; } pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index d549d6271d89..513d015a5ace 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -54,8 +54,7 @@ static void ion_page_pool_free_pages(struct ion_page_pool *pool, __free_pages(page, pool->order); } -static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page, - bool prefetch) +static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) { mutex_lock(&pool->mutex); if (PageHighMem(page)) { @@ -65,15 +64,11 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page, list_add_tail(&page->lru, &pool->low_items); pool->low_count++; } - if (!prefetch) - pool->nr_unreserved++; - mutex_unlock(&pool->mutex); return 0; } -static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high, - bool prefetch) +static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) { struct page *page; @@ -87,13 +82,6 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high, pool->low_count--; } - if (prefetch) { - BUG_ON(!pool->nr_unreserved); - pool->nr_unreserved--; - } - pool->nr_unreserved = min_t(int, pool->high_count + pool->low_count, - pool->nr_unreserved); - list_del(&page->lru); return page; } @@ -108,9 +96,9 @@ void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool) if (mutex_trylock(&pool->mutex)) { if (pool->high_count) - page = ion_page_pool_remove(pool, true, false); + page = ion_page_pool_remove(pool, true); else if (pool->low_count) - page = ion_page_pool_remove(pool, false, false); + page = ion_page_pool_remove(pool, false); mutex_unlock(&pool->mutex); } if (!page) { @@ -120,27 +108,6 @@ void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool) return page; } -void *ion_page_pool_prefetch(struct ion_page_pool *pool, bool *from_pool) -{ - struct page *page = NULL; - - BUG_ON(!pool); - - *from_pool = true; - - if (mutex_trylock(&pool->mutex)) { - if (pool->high_count && pool->nr_unreserved > 0) - page = ion_page_pool_remove(pool, true, true); - else if (pool->low_count && pool->nr_unreserved > 0) - page = ion_page_pool_remove(pool, false, true); - mutex_unlock(&pool->mutex); - } - if (!page) { - page = ion_page_pool_alloc_pages(pool); - *from_pool = false; - } - return page; -} /* * Tries to allocate from only the specified Pool and returns NULL otherwise */ @@ -152,24 +119,20 @@ void *ion_page_pool_alloc_pool_only(struct ion_page_pool *pool) if (mutex_trylock(&pool->mutex)) { if (pool->high_count) - page = ion_page_pool_remove(pool, true, false); + page = ion_page_pool_remove(pool, true); else if (pool->low_count) - page = ion_page_pool_remove(pool, false, false); + page = ion_page_pool_remove(pool, false); mutex_unlock(&pool->mutex); } return page; } -void ion_page_pool_free(struct ion_page_pool *pool, struct page *page, - bool prefetch) +void ion_page_pool_free(struct ion_page_pool *pool, struct page *page) { int ret; - BUG_ON(pool->order != compound_order(page)); - - ret = ion_page_pool_add(pool, page, prefetch); - /* FIXME? For a secure page, not hyp unassigned in this err path */ + ret = ion_page_pool_add(pool, page); if (ret) ion_page_pool_free_pages(pool, page); } @@ -208,9 +171,9 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, mutex_lock(&pool->mutex); if (pool->low_count) { - page = ion_page_pool_remove(pool, false, false); + page = ion_page_pool_remove(pool, false); } else if (high && pool->high_count) { - page = ion_page_pool_remove(pool, true, false); + page = ion_page_pool_remove(pool, true); } else { mutex_unlock(&pool->mutex); break; @@ -233,10 +196,9 @@ struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, pool->dev = dev; pool->high_count = 0; pool->low_count = 0; - pool->nr_unreserved = 0; INIT_LIST_HEAD(&pool->low_items); INIT_LIST_HEAD(&pool->high_items); - pool->gfp_mask = gfp_mask | __GFP_COMP; + pool->gfp_mask = gfp_mask; pool->order = order; mutex_init(&pool->mutex); plist_node_init(&pool->list, order); diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index 4f383661258a..69da287c064f 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -415,8 +415,6 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, * struct ion_page_pool - pagepool struct * @high_count: number of highmem items in the pool * @low_count: number of lowmem items in the pool - * @nr_unreserved: number of items in the pool which have not been reserved - * by a prefetch allocation * @high_items: list of highmem items * @low_items: list of lowmem items * @mutex: lock protecting this struct and especially the count @@ -433,7 +431,6 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, struct ion_page_pool { int high_count; int low_count; - int nr_unreserved; struct list_head high_items; struct list_head low_items; struct mutex mutex; @@ -448,10 +445,9 @@ struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, void ion_page_pool_destroy(struct ion_page_pool *); void *ion_page_pool_alloc(struct ion_page_pool *, bool *from_pool); void *ion_page_pool_alloc_pool_only(struct ion_page_pool *); -void ion_page_pool_free(struct ion_page_pool *, struct page *, bool prefetch); +void ion_page_pool_free(struct ion_page_pool *, struct page *); void ion_page_pool_free_immediate(struct ion_page_pool *, struct page *); int ion_page_pool_total(struct ion_page_pool *pool, bool high); -void *ion_page_pool_prefetch(struct ion_page_pool *pool, bool *from_pool); #ifdef CONFIG_ION_POOL_CACHE_POLICY static inline void ion_page_pool_alloc_set_cache_policy diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index ff75e1690f59..981cb2f622cb 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -63,6 +63,8 @@ struct ion_system_heap { struct ion_page_pool **uncached_pools; struct ion_page_pool **cached_pools; struct ion_page_pool **secure_pools[VMID_LAST]; + /* Prevents unnecessary page splitting */ + struct mutex split_page_mutex; }; struct page_info { @@ -78,7 +80,6 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, bool *from_pool) { bool cached = ion_buffer_cached(buffer); - bool prefetch = buffer->flags & ION_FLAG_POOL_PREFETCH; struct page *page; struct ion_page_pool *pool; int vmid = get_secure_vmid(buffer->flags); @@ -92,10 +93,7 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap, else pool = heap->cached_pools[order_to_index(order)]; - if (prefetch) - page = ion_page_pool_prefetch(pool, from_pool); - else - page = ion_page_pool_alloc(pool, from_pool); + page = ion_page_pool_alloc(pool, from_pool); } else { gfp_t gfp_mask = low_order_gfp_flags; if (order) @@ -119,7 +117,6 @@ static void free_buffer_page(struct ion_system_heap *heap, unsigned int order) { bool cached = ion_buffer_cached(buffer); - bool prefetch = buffer->flags & ION_FLAG_POOL_PREFETCH; int vmid = get_secure_vmid(buffer->flags); if (!(buffer->flags & ION_FLAG_POOL_FORCE_ALLOC)) { @@ -134,12 +131,65 @@ static void free_buffer_page(struct ion_system_heap *heap, if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) ion_page_pool_free_immediate(pool, page); else - ion_page_pool_free(pool, page, prefetch); + ion_page_pool_free(pool, page); } else { __free_pages(page, order); } } +static struct page *alloc_from_secure_pool_order(struct ion_system_heap *heap, + struct ion_buffer *buffer, + unsigned long order) +{ + int vmid = get_secure_vmid(buffer->flags); + struct ion_page_pool *pool; + + if (!is_secure_vmid_valid(vmid)) + return NULL; + + pool = heap->secure_pools[vmid][order_to_index(order)]; + return ion_page_pool_alloc_pool_only(pool); +} + +static struct page *split_page_from_secure_pool(struct ion_system_heap *heap, + struct ion_buffer *buffer) +{ + int i, j; + struct page *page; + unsigned int order; + + mutex_lock(&heap->split_page_mutex); + + /* + * Someone may have just split a page and returned the unused portion + * back to the pool, so try allocating from the pool one more time + * before splitting. We want to maintain large pages sizes when + * possible. + */ + page = alloc_from_secure_pool_order(heap, buffer, 0); + if (page) + goto got_page; + + for (i = num_orders - 2; i >= 0; i--) { + order = orders[i]; + page = alloc_from_secure_pool_order(heap, buffer, order); + if (!page) + continue; + + split_page(page, order); + break; + } + /* Return the remaining order-0 pages to the pool */ + if (page) + for (j = 1; j < (1 << order); j++) + free_buffer_page(heap, buffer, page + j, 0); + +got_page: + mutex_unlock(&heap->split_page_mutex); + + return page; +} + static struct page_info *alloc_largest_available(struct ion_system_heap *heap, struct ion_buffer *buffer, unsigned long size, @@ -174,6 +224,49 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap, return NULL; } + +static struct page_info *alloc_from_pool_preferred( + struct ion_system_heap *heap, struct ion_buffer *buffer, + unsigned long size, unsigned int max_order) +{ + struct page *page; + struct page_info *info; + int i; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + + for (i = 0; i < num_orders; i++) { + if (size < order_to_size(orders[i])) + continue; + if (max_order < orders[i]) + continue; + + page = alloc_from_secure_pool_order(heap, buffer, orders[i]); + if (!page) + continue; + + info->page = page; + info->order = orders[i]; + info->from_pool = true; + INIT_LIST_HEAD(&info->list); + return info; + } + + page = split_page_from_secure_pool(heap, buffer); + if (page) { + info->page = page; + info->order = 0; + info->from_pool = true; + INIT_LIST_HEAD(&info->list); + return info; + } + + kfree(info); + return alloc_largest_available(heap, buffer, size, max_order); +} + static unsigned int process_info(struct page_info *info, struct scatterlist *sg, struct scatterlist *sg_sync, @@ -236,9 +329,17 @@ static int ion_system_heap_allocate(struct ion_heap *heap, data.size = 0; INIT_LIST_HEAD(&pages); INIT_LIST_HEAD(&pages_from_pool); + while (size_remaining > 0) { - info = alloc_largest_available(sys_heap, buffer, size_remaining, - max_order); + if (is_secure_vmid_valid(vmid)) + info = alloc_from_pool_preferred( + sys_heap, buffer, size_remaining, + max_order); + else + info = alloc_largest_available( + sys_heap, buffer, size_remaining, + max_order); + if (!info) goto err; @@ -452,7 +553,7 @@ out1: /* Restore pages to secure pool */ list_for_each_entry_safe(page, tmp, &pages, lru) { list_del(&page->lru); - ion_page_pool_free(pool, page, false); + ion_page_pool_free(pool, page); } return 0; out2: @@ -692,6 +793,8 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *data) if (ion_system_heap_create_pools(dev, heap->cached_pools)) goto err_create_cached_pools; + mutex_init(&heap->split_page_mutex); + heap->heap.debug_show = ion_system_heap_debug_show; return &heap->heap; diff --git a/drivers/staging/android/ion/ion_system_secure_heap.c b/drivers/staging/android/ion/ion_system_secure_heap.c index 5c0225cd4e24..9570ab520ca2 100644 --- a/drivers/staging/android/ion/ion_system_secure_heap.c +++ b/drivers/staging/android/ion/ion_system_secure_heap.c @@ -151,8 +151,7 @@ static void ion_system_secure_heap_prefetch_work(struct work_struct *work) /* buffer->heap used by free() */ buffer->heap = &secure_heap->heap; - buffer->flags = ION_FLAG_POOL_PREFETCH; - buffer->flags |= vmid_flags; + buffer->flags = vmid_flags; ret = sys_heap->ops->allocate(sys_heap, buffer, size, PAGE_SIZE, 0); if (ret) { diff --git a/drivers/staging/android/uapi/msm_ion.h b/drivers/staging/android/uapi/msm_ion.h index 73b4d1edcaad..b3c29826834e 100644 --- a/drivers/staging/android/uapi/msm_ion.h +++ b/drivers/staging/android/uapi/msm_ion.h @@ -110,8 +110,6 @@ enum cp_mem_usage { */ #define ION_FLAG_POOL_FORCE_ALLOC (1 << 16) -#define ION_FLAG_POOL_PREFETCH (1 << 27) - /** * Deprecated! Please use the corresponding ION_FLAG_* */ @@ -176,7 +174,6 @@ struct ion_prefetch_regions { struct ion_prefetch_data { int heap_id; unsigned long len; - /* Is unsigned long bad? 32bit compiler vs 64 bit compiler*/ struct ion_prefetch_regions __user *regions; unsigned int nr_regions; }; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 899a77187bde..2a2975c352f4 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2227,7 +2227,6 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, device->con->write = pl011_early_write; return 0; } -EARLYCON_DECLARE(pl011, pl011_early_console_setup); OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); #else diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 03ebe401fff7..3a1de5c87cb4 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -576,7 +576,6 @@ static int __init arc_early_console_setup(struct earlycon_device *dev, dev->con->write = arc_early_serial_write; return 0; } -EARLYCON_DECLARE(arc_uart, arc_early_console_setup); OF_EARLYCON_DECLARE(arc_uart, "snps,arc-uart", arc_early_console_setup); #endif /* CONFIG_SERIAL_ARC_CONSOLE */ diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index b5b2f2be6be7..4d2e3e7a40bb 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -19,7 +19,6 @@ #include <linux/io.h> #include <linux/serial_core.h> #include <linux/sizes.h> -#include <linux/mod_devicetable.h> #ifdef CONFIG_FIX_EARLYCON_MEM #include <asm/fixmap.h> @@ -37,13 +36,6 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; -extern struct earlycon_id __earlycon_table[]; -static const struct earlycon_id __earlycon_table_sentinel - __used __section(__earlycon_table_end); - -static const struct of_device_id __earlycon_of_table_sentinel - __used __section(__earlycon_of_table_end); - static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) { void __iomem *base; @@ -159,7 +151,7 @@ int __init setup_earlycon(char *buf) if (early_con.flags & CON_ENABLED) return -EALREADY; - for (match = __earlycon_table; match->name[0]; match++) { + for (match = __earlycon_table; match < __earlycon_table_end; match++) { size_t len = strlen(match->name); if (strncmp(buf, match->name, len)) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index edd66bb7bd4c..670eda466438 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -19,33 +19,147 @@ # define SUPPORT_SYSRQ #endif +#include <linux/kernel.h> #include <linux/atomic.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> -#include <linux/hrtimer.h> #include <linux/module.h> #include <linux/io.h> #include <linux/ioport.h> -#include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/init.h> #include <linux/console.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> -#include <linux/serial.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/of.h> #include <linux/of_device.h> - -#include "msm_serial.h" - -#define UARTDM_BURST_SIZE 16 /* in bytes */ -#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */ -#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ -#define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4) +#include <linux/wait.h> + +#define UART_MR1 0x0000 + +#define UART_MR1_AUTO_RFR_LEVEL0 0x3F +#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 +#define UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00 +#define UART_MR1_RX_RDY_CTL BIT(7) +#define UART_MR1_CTS_CTL BIT(6) + +#define UART_MR2 0x0004 +#define UART_MR2_ERROR_MODE BIT(6) +#define UART_MR2_BITS_PER_CHAR 0x30 +#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) +#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) +#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) +#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) +#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) +#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) +#define UART_MR2_PARITY_MODE_NONE 0x0 +#define UART_MR2_PARITY_MODE_ODD 0x1 +#define UART_MR2_PARITY_MODE_EVEN 0x2 +#define UART_MR2_PARITY_MODE_SPACE 0x3 +#define UART_MR2_PARITY_MODE 0x3 + +#define UART_CSR 0x0008 + +#define UART_TF 0x000C +#define UARTDM_TF 0x0070 + +#define UART_CR 0x0010 +#define UART_CR_CMD_NULL (0 << 4) +#define UART_CR_CMD_RESET_RX (1 << 4) +#define UART_CR_CMD_RESET_TX (2 << 4) +#define UART_CR_CMD_RESET_ERR (3 << 4) +#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) +#define UART_CR_CMD_START_BREAK (5 << 4) +#define UART_CR_CMD_STOP_BREAK (6 << 4) +#define UART_CR_CMD_RESET_CTS (7 << 4) +#define UART_CR_CMD_RESET_STALE_INT (8 << 4) +#define UART_CR_CMD_PACKET_MODE (9 << 4) +#define UART_CR_CMD_MODE_RESET (12 << 4) +#define UART_CR_CMD_SET_RFR (13 << 4) +#define UART_CR_CMD_RESET_RFR (14 << 4) +#define UART_CR_CMD_PROTECTION_EN (16 << 4) +#define UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8) +#define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) +#define UART_CR_CMD_FORCE_STALE (4 << 8) +#define UART_CR_CMD_RESET_TX_READY (3 << 8) +#define UART_CR_TX_DISABLE BIT(3) +#define UART_CR_TX_ENABLE BIT(2) +#define UART_CR_RX_DISABLE BIT(1) +#define UART_CR_RX_ENABLE BIT(0) +#define UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4)) + +#define UART_IMR 0x0014 +#define UART_IMR_TXLEV BIT(0) +#define UART_IMR_RXSTALE BIT(3) +#define UART_IMR_RXLEV BIT(4) +#define UART_IMR_DELTA_CTS BIT(5) +#define UART_IMR_CURRENT_CTS BIT(6) +#define UART_IMR_RXBREAK_START BIT(10) + +#define UART_IPR_RXSTALE_LAST 0x20 +#define UART_IPR_STALE_LSB 0x1F +#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 +#define UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80 + +#define UART_IPR 0x0018 +#define UART_TFWR 0x001C +#define UART_RFWR 0x0020 +#define UART_HCR 0x0024 + +#define UART_MREG 0x0028 +#define UART_NREG 0x002C +#define UART_DREG 0x0030 +#define UART_MNDREG 0x0034 +#define UART_IRDA 0x0038 +#define UART_MISR_MODE 0x0040 +#define UART_MISR_RESET 0x0044 +#define UART_MISR_EXPORT 0x0048 +#define UART_MISR_VAL 0x004C +#define UART_TEST_CTRL 0x0050 + +#define UART_SR 0x0008 +#define UART_SR_HUNT_CHAR BIT(7) +#define UART_SR_RX_BREAK BIT(6) +#define UART_SR_PAR_FRAME_ERR BIT(5) +#define UART_SR_OVERRUN BIT(4) +#define UART_SR_TX_EMPTY BIT(3) +#define UART_SR_TX_READY BIT(2) +#define UART_SR_RX_FULL BIT(1) +#define UART_SR_RX_READY BIT(0) + +#define UART_RF 0x000C +#define UARTDM_RF 0x0070 +#define UART_MISR 0x0010 +#define UART_ISR 0x0014 +#define UART_ISR_TX_READY BIT(7) + +#define UARTDM_RXFS 0x50 +#define UARTDM_RXFS_BUF_SHIFT 0x7 +#define UARTDM_RXFS_BUF_MASK 0x7 + +#define UARTDM_DMEN 0x3C +#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) +#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) + +#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */ +#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */ + +#define UARTDM_DMEN_RX_BAM_ENABLE BIT(3) /* UARTDM_1P4 */ +#define UARTDM_DMEN_RX_DM_ENABLE BIT(1) /* < UARTDM_1P4 */ + +#define UARTDM_DMRX 0x34 +#define UARTDM_NCF_TX 0x40 +#define UARTDM_RX_TOTAL_SNAP 0x38 + +#define UARTDM_BURST_SIZE 16 /* in bytes */ +#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */ +#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ +#define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4) enum { UARTDM_1P1 = 1, @@ -78,10 +192,65 @@ struct msm_port { struct msm_dma rx_dma; }; +#define UART_TO_MSM(uart_port) container_of(uart_port, struct msm_port, uart) + +static +void msm_write(struct uart_port *port, unsigned int val, unsigned int off) +{ + writel_relaxed(val, port->membase + off); +} + +static +unsigned int msm_read(struct uart_port *port, unsigned int off) +{ + return readl_relaxed(port->membase + off); +} + +/* + * Setup the MND registers to use the TCXO clock. + */ +static void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) +{ + msm_write(port, 0x06, UART_MREG); + msm_write(port, 0xF1, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x1A, UART_MNDREG); + port->uartclk = 1843200; +} + +/* + * Setup the MND registers to use the TCXO clock divided by 4. + */ +static void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) +{ + msm_write(port, 0x18, UART_MREG); + msm_write(port, 0xF6, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x0A, UART_MNDREG); + port->uartclk = 1843200; +} + +static void msm_serial_set_mnd_regs(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + /* + * These registers don't exist so we change the clk input rate + * on uartdm hardware instead + */ + if (msm_port->is_uartdm) + return; + + if (port->uartclk == 19200000) + msm_serial_set_mnd_regs_tcxo(port); + else if (port->uartclk == 4800000) + msm_serial_set_mnd_regs_tcxoby4(port); +} + static void msm_handle_tx(struct uart_port *port); static void msm_start_rx_dma(struct msm_port *msm_port); -void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) +static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) { struct device *dev = port->dev; unsigned int mapped; @@ -388,10 +557,6 @@ static void msm_complete_rx_dma(void *args) val &= ~dma->enable_bit; msm_write(port, val, UARTDM_DMEN); - /* Restore interrupts */ - msm_port->imr |= UART_IMR_RXLEV | UART_IMR_RXSTALE; - msm_write(port, msm_port->imr, UART_IMR); - if (msm_read(port, UART_SR) & UART_SR_OVERRUN) { port->icount.overrun++; tty_insert_flip_char(tport, 0, TTY_OVERRUN); @@ -726,7 +891,7 @@ static void msm_handle_tx(struct uart_port *port) return; } - pio_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); dma_min = 1; /* Always DMA */ @@ -861,37 +1026,72 @@ struct msm_baud_map { }; static const struct msm_baud_map * -msm_find_best_baud(struct uart_port *port, unsigned int baud) +msm_find_best_baud(struct uart_port *port, unsigned int baud, + unsigned long *rate) { - unsigned int i, divisor; - const struct msm_baud_map *entry; + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int divisor, result; + unsigned long target, old, best_rate = 0, diff, best_diff = ULONG_MAX; + const struct msm_baud_map *entry, *end, *best; static const struct msm_baud_map table[] = { - { 1536, 0x00, 1 }, - { 768, 0x11, 1 }, - { 384, 0x22, 1 }, - { 192, 0x33, 1 }, - { 96, 0x44, 1 }, - { 48, 0x55, 1 }, - { 32, 0x66, 1 }, - { 24, 0x77, 1 }, - { 16, 0x88, 1 }, - { 12, 0x99, 6 }, - { 8, 0xaa, 6 }, - { 6, 0xbb, 6 }, - { 4, 0xcc, 6 }, - { 3, 0xdd, 8 }, - { 2, 0xee, 16 }, { 1, 0xff, 31 }, - { 0, 0xff, 31 }, + { 2, 0xee, 16 }, + { 3, 0xdd, 8 }, + { 4, 0xcc, 6 }, + { 6, 0xbb, 6 }, + { 8, 0xaa, 6 }, + { 12, 0x99, 6 }, + { 16, 0x88, 1 }, + { 24, 0x77, 1 }, + { 32, 0x66, 1 }, + { 48, 0x55, 1 }, + { 96, 0x44, 1 }, + { 192, 0x33, 1 }, + { 384, 0x22, 1 }, + { 768, 0x11, 1 }, + { 1536, 0x00, 1 }, }; - divisor = uart_get_divisor(port, baud); + best = table; /* Default to smallest divider */ + target = clk_round_rate(msm_port->clk, 16 * baud); + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + + end = table + ARRAY_SIZE(table); + entry = table; + while (entry < end) { + if (entry->divisor <= divisor) { + result = target / entry->divisor / 16; + diff = abs(result - baud); + + /* Keep track of best entry */ + if (diff < best_diff) { + best_diff = diff; + best = entry; + best_rate = target; + } - for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++) - if (entry->divisor <= divisor) - break; + if (result == baud) + break; + } else if (entry->divisor > divisor) { + old = target; + target = clk_round_rate(msm_port->clk, old + 1); + /* + * The rate didn't get any faster so we can't do + * better at dividing it down + */ + if (target == old) + break; - return entry; /* Default to smallest divider */ + /* Start the divisor search over at this new rate */ + entry = table; + divisor = DIV_ROUND_CLOSEST(target, 16 * baud); + continue; + } + entry++; + } + + *rate = best_rate; + return best; } static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, @@ -900,22 +1100,20 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; - unsigned long flags; - - entry = msm_find_best_baud(port, baud); - - msm_write(port, entry->code, UART_CSR); - - if (baud > 460800) - port->uartclk = baud * 16; + unsigned long flags, rate; flags = *saved_flags; spin_unlock_irqrestore(&port->lock, flags); - clk_set_rate(msm_port->clk, port->uartclk); + entry = msm_find_best_baud(port, baud, &rate); + clk_set_rate(msm_port->clk, rate); + baud = rate / 16 / entry->divisor; spin_lock_irqsave(&port->lock, flags); *saved_flags = flags; + port->uartclk = rate; + + msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ rxstale = entry->rxstale; @@ -1480,7 +1678,6 @@ msm_serial_early_console_setup(struct earlycon_device *device, const char *opt) device->con->write = msm_serial_early_write; return 0; } -EARLYCON_DECLARE(msm_serial, msm_serial_early_console_setup); OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart", msm_serial_early_console_setup); @@ -1502,7 +1699,6 @@ msm_serial_early_console_setup_dm(struct earlycon_device *device, device->con->write = msm_serial_early_write_dm; return 0; } -EARLYCON_DECLARE(msm_serial_dm, msm_serial_early_console_setup_dm); OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", msm_serial_early_console_setup_dm); @@ -1581,8 +1777,6 @@ static int msm_serial_probe(struct platform_device *pdev) msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_port->pclk)) return PTR_ERR(msm_port->pclk); - - clk_set_rate(msm_port->clk, 1843200); } port->uartclk = clk_get_rate(msm_port->clk); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h deleted file mode 100644 index dfdf73707e9d..000000000000 --- a/drivers/tty/serial/msm_serial.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2007 Google, Inc. - * Author: Robert Love <rlove@google.com> - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __DRIVERS_SERIAL_MSM_SERIAL_H -#define __DRIVERS_SERIAL_MSM_SERIAL_H - -#define UART_MR1 0x0000 - -#define UART_MR1_AUTO_RFR_LEVEL0 0x3F -#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 -#define UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00 -#define UART_MR1_RX_RDY_CTL BIT(7) -#define UART_MR1_CTS_CTL BIT(6) - -#define UART_MR2 0x0004 -#define UART_MR2_ERROR_MODE BIT(6) -#define UART_MR2_BITS_PER_CHAR 0x30 -#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) -#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) -#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) -#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) -#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) -#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) -#define UART_MR2_PARITY_MODE_NONE 0x0 -#define UART_MR2_PARITY_MODE_ODD 0x1 -#define UART_MR2_PARITY_MODE_EVEN 0x2 -#define UART_MR2_PARITY_MODE_SPACE 0x3 -#define UART_MR2_PARITY_MODE 0x3 - -#define UART_CSR 0x0008 - -#define UART_TF 0x000C -#define UARTDM_TF 0x0070 - -#define UART_CR 0x0010 -#define UART_CR_CMD_NULL (0 << 4) -#define UART_CR_CMD_RESET_RX (1 << 4) -#define UART_CR_CMD_RESET_TX (2 << 4) -#define UART_CR_CMD_RESET_ERR (3 << 4) -#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) -#define UART_CR_CMD_START_BREAK (5 << 4) -#define UART_CR_CMD_STOP_BREAK (6 << 4) -#define UART_CR_CMD_RESET_CTS (7 << 4) -#define UART_CR_CMD_RESET_STALE_INT (8 << 4) -#define UART_CR_CMD_PACKET_MODE (9 << 4) -#define UART_CR_CMD_MODE_RESET (12 << 4) -#define UART_CR_CMD_SET_RFR (13 << 4) -#define UART_CR_CMD_RESET_RFR (14 << 4) -#define UART_CR_CMD_PROTECTION_EN (16 << 4) -#define UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8) -#define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) -#define UART_CR_CMD_FORCE_STALE (4 << 8) -#define UART_CR_CMD_RESET_TX_READY (3 << 8) -#define UART_CR_TX_DISABLE BIT(3) -#define UART_CR_TX_ENABLE BIT(2) -#define UART_CR_RX_DISABLE BIT(1) -#define UART_CR_RX_ENABLE BIT(0) -#define UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4)) - -#define UART_IMR 0x0014 -#define UART_IMR_TXLEV BIT(0) -#define UART_IMR_RXSTALE BIT(3) -#define UART_IMR_RXLEV BIT(4) -#define UART_IMR_DELTA_CTS BIT(5) -#define UART_IMR_CURRENT_CTS BIT(6) -#define UART_IMR_RXBREAK_START BIT(10) - -#define UART_IPR_RXSTALE_LAST 0x20 -#define UART_IPR_STALE_LSB 0x1F -#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 -#define UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80 - -#define UART_IPR 0x0018 -#define UART_TFWR 0x001C -#define UART_RFWR 0x0020 -#define UART_HCR 0x0024 - -#define UART_MREG 0x0028 -#define UART_NREG 0x002C -#define UART_DREG 0x0030 -#define UART_MNDREG 0x0034 -#define UART_IRDA 0x0038 -#define UART_MISR_MODE 0x0040 -#define UART_MISR_RESET 0x0044 -#define UART_MISR_EXPORT 0x0048 -#define UART_MISR_VAL 0x004C -#define UART_TEST_CTRL 0x0050 - -#define UART_SR 0x0008 -#define UART_SR_HUNT_CHAR BIT(7) -#define UART_SR_RX_BREAK BIT(6) -#define UART_SR_PAR_FRAME_ERR BIT(5) -#define UART_SR_OVERRUN BIT(4) -#define UART_SR_TX_EMPTY BIT(3) -#define UART_SR_TX_READY BIT(2) -#define UART_SR_RX_FULL BIT(1) -#define UART_SR_RX_READY BIT(0) - -#define UART_RF 0x000C -#define UARTDM_RF 0x0070 -#define UART_MISR 0x0010 -#define UART_ISR 0x0014 -#define UART_ISR_TX_READY BIT(7) - -#define UARTDM_RXFS 0x50 -#define UARTDM_RXFS_BUF_SHIFT 0x7 -#define UARTDM_RXFS_BUF_MASK 0x7 - -#define UARTDM_DMEN 0x3C -#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) -#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) - -#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */ -#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */ - -#define UARTDM_DMEN_RX_BAM_ENABLE BIT(3) /* UARTDM_1P4 */ -#define UARTDM_DMEN_RX_DM_ENABLE BIT(1) /* < UARTDM_1P4 */ - -#define UARTDM_DMRX 0x34 -#define UARTDM_NCF_TX 0x40 -#define UARTDM_RX_TOTAL_SNAP 0x38 - -#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) - -static inline -void msm_write(struct uart_port *port, unsigned int val, unsigned int off) -{ - writel_relaxed_no_log(val, port->membase + off); -} - -static inline -unsigned int msm_read(struct uart_port *port, unsigned int off) -{ - return readl_relaxed_no_log(port->membase + off); -} - -/* - * Setup the MND registers to use the TCXO clock. - */ -static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) -{ - msm_write(port, 0x06, UART_MREG); - msm_write(port, 0xF1, UART_NREG); - msm_write(port, 0x0F, UART_DREG); - msm_write(port, 0x1A, UART_MNDREG); - port->uartclk = 1843200; -} - -/* - * Setup the MND registers to use the TCXO clock divided by 4. - */ -static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) -{ - msm_write(port, 0x18, UART_MREG); - msm_write(port, 0xF6, UART_NREG); - msm_write(port, 0x0F, UART_DREG); - msm_write(port, 0x0A, UART_MNDREG); - port->uartclk = 1843200; -} - -static inline -void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) -{ - if (port->uartclk == 19200000) - msm_serial_set_mnd_regs_tcxo(port); - else if (port->uartclk == 4800000) - msm_serial_set_mnd_regs_tcxoby4(port); -} - -#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk - -#endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d72cd736bdc6..fd9c47f2f29f 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -2451,7 +2451,6 @@ static int __init s3c2410_early_console_setup(struct earlycon_device *device, } OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart", s3c2410_early_console_setup); -EARLYCON_DECLARE(s3c2410, s3c2410_early_console_setup); /* S3C2412, S3C2440, S3C64xx */ static struct samsung_early_console_data s3c2440_early_console_data = { @@ -2470,9 +2469,6 @@ OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart", s3c2440_early_console_setup); OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart", s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c2412, s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c2440, s3c2440_early_console_setup); -EARLYCON_DECLARE(s3c6400, s3c2440_early_console_setup); /* S5PV210, EXYNOS */ static struct samsung_early_console_data s5pv210_early_console_data = { @@ -2489,8 +2485,6 @@ OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart", s5pv210_early_console_setup); OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", s5pv210_early_console_setup); -EARLYCON_DECLARE(s5pv210, s5pv210_early_console_setup); -EARLYCON_DECLARE(exynos4210, s5pv210_early_console_setup); #endif MODULE_ALIAS("platform:samsung-uart"); diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 9dbae01d41ce..9ae182a54784 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -624,8 +624,6 @@ static int __init sprd_early_console_setup( device->con->write = sprd_early_write; return 0; } - -EARLYCON_DECLARE(sprd_serial, sprd_early_console_setup); OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart", sprd_early_console_setup); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 7bc6b5340dd7..b011efe189e7 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -11,6 +11,7 @@ */ #include <linux/delay.h> +#include <linux/hrtimer.h> #include <linux/ipc_logging.h> #include <linux/kernel.h> #include <linux/list.h> @@ -158,14 +159,15 @@ static void *usbpd_ipc_log; /* Timeouts (in ms) */ #define ERROR_RECOVERY_TIME 25 -#define SENDER_RESPONSE_TIME 30 +#define SENDER_RESPONSE_TIME 26 #define SINK_WAIT_CAP_TIME 620 -#define PS_TRANSITION_TIME 550 +#define PS_TRANSITION_TIME 450 #define SRC_CAP_TIME 120 #define SRC_TRANSITION_TIME 25 -#define PS_HARD_RESET_TIME 35 +#define SRC_RECOVER_TIME 660 +#define PS_HARD_RESET_TIME 25 #define PS_SOURCE_ON 400 -#define PS_SOURCE_OFF 900 +#define PS_SOURCE_OFF 750 #define VDM_BUSY_TIME 50 #define PD_CAPS_COUNT 50 @@ -240,9 +242,6 @@ static void *usbpd_ipc_log; static int min_sink_current = 900; module_param(min_sink_current, int, S_IRUSR | S_IWUSR); -static int max_sink_current = 3000; -module_param(max_sink_current, int, S_IRUSR | S_IWUSR); - static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */ @@ -258,7 +257,8 @@ struct vdm_tx { struct usbpd { struct device dev; struct workqueue_struct *wq; - struct delayed_work sm_work; + struct work_struct sm_work; + struct hrtimer timer; struct extcon_dev *extcon; @@ -406,8 +406,8 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, static int pd_select_pdo(struct usbpd *pd, int pdo_pos) { - int curr = min_sink_current; - int max_current = max_sink_current; + int curr; + int max_current; bool mismatch = false; u32 pdo = pd->received_pdos[pdo_pos - 1]; @@ -417,18 +417,19 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) return -ENOTSUPP; } + curr = max_current = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + /* * Check if the PDO has enough current, otherwise set the * Capability Mismatch flag */ - if ((PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10) < curr) { + if (curr < min_sink_current) { mismatch = true; - max_current = curr; - curr = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + max_current = min_sink_current; } pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; - pd->requested_current = max_current; + pd->requested_current = curr; pd->requested_pdo = pdo_pos; pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10, max_current / 10); @@ -485,7 +486,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset = true; - mod_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); } static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, @@ -533,7 +534,7 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, pd->rx_msg_len = PD_MSG_HDR_COUNT(header); memcpy(&pd->rx_payload, buf, len); - mod_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); } static void phy_shutdown(struct usbpd *pd) @@ -541,6 +542,16 @@ static void phy_shutdown(struct usbpd *pd) usbpd_dbg(&pd->dev, "shutdown"); } +static enum hrtimer_restart pd_timeout(struct hrtimer *timer) +{ + struct usbpd *pd = container_of(timer, struct usbpd, timer); + + usbpd_dbg(&pd->dev, "timeout"); + queue_work(pd->wq, &pd->sm_work); + + return HRTIMER_NORESTART; +} + /* Enters new state and executes actions on entry */ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) { @@ -564,8 +575,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_ERROR_RECOVERY: /* perform hard disconnect/reconnect */ pd->in_pr_swap = false; set_power_role(pd, PR_NONE); - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(ERROR_RECOVERY_TIME)); + pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; + queue_work(pd->wq, &pd->sm_work); break; /* Source states */ @@ -611,7 +622,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) /* fall-through */ case PE_SRC_SEND_CAPABILITIES: - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); break; case PE_SRC_NEGOTIATE_CAPABILITY: @@ -638,16 +649,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; } - /* - * we only support VSafe5V so Aceept right away as there is - * nothing more to prepare from the power supply - */ - pd->current_state = PE_SRC_TRANSITION_SUPPLY; - usbpd_dbg(&pd->dev, "Enter %s\n", - usbpd_state_strings[pd->current_state]); - /* fall-through */ - - case PE_SRC_TRANSITION_SUPPLY: + /* PE_SRC_TRANSITION_SUPPLY pseudo-state */ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); @@ -655,8 +657,23 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; } - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SRC_TRANSITION_TIME)); + /* tSrcTransition required after ACCEPT */ + usleep_range(SRC_TRANSITION_TIME * USEC_PER_MSEC, + (SRC_TRANSITION_TIME + 5) * USEC_PER_MSEC); + + /* + * Normally a voltage change should occur within tSrcReady + * but since we only support VSafe5V there is nothing more to + * prepare from the power supply so send PS_RDY right away. + */ + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending PS_RDY\n"); + usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); + break; + } + + usbpd_set_state(pd, PE_SRC_READY); break; case PE_SRC_READY: @@ -685,7 +702,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd_phy_update_roles(pd->current_dr, pd->current_pr); } - msleep(1000); /* tSrcRecover */ + msleep(SRC_RECOVER_TIME); ret = regulator_enable(pd->vbus); if (ret) @@ -705,7 +722,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* hard reset may sleep; handle it in the workqueue */ - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); break; case PE_SRC_SEND_SOFT_RESET: @@ -723,8 +740,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } /* wait for ACCEPT */ - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SENDER_RESPONSE_TIME * 3)); + hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), + HRTIMER_MODE_REL); break; /* Sink states */ @@ -773,8 +790,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) if (!pd->vbus_present) { /* can get here during a hard reset and we lost vbus */ pd->current_state = PE_SNK_DISCOVERY; - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(2000)); + hrtimer_start(&pd->timer, ms_to_ktime(2000), + HRTIMER_MODE_REL); break; } @@ -787,10 +804,11 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_WAIT_FOR_CAPABILITIES: if (pd->rx_msg_len && pd->rx_msg_type) - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); else - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SINK_WAIT_CAP_TIME)); + hrtimer_start(&pd->timer, + ms_to_ktime(SINK_WAIT_CAP_TIME), + HRTIMER_MODE_REL); break; case PE_SNK_EVALUATE_CAPABILITY: @@ -812,14 +830,14 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) usbpd_err(&pd->dev, "Error sending Request\n"); /* wait for ACCEPT */ - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SENDER_RESPONSE_TIME * 3)); + hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), + HRTIMER_MODE_REL); break; case PE_SNK_TRANSITION_SINK: /* wait for PS_RDY */ - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(PS_TRANSITION_TIME)); + hrtimer_start(&pd->timer, ms_to_ktime(PS_TRANSITION_TIME), + HRTIMER_MODE_REL); break; case PE_SNK_READY: @@ -912,7 +930,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) /* VDM will get sent in PE_SRC/SNK_READY state handling */ list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue); - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); return 0; } @@ -1094,8 +1112,8 @@ static void handle_vdm_rx(struct usbpd *pd) /* wait tVDMBusy, then retry */ list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue); pd->vdm_tx_retry = NULL; - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(VDM_BUSY_TIME)); + hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME), + HRTIMER_MODE_REL); break; default: break; @@ -1197,7 +1215,7 @@ static void dr_swap(struct usbpd *pd) /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { - struct usbpd *pd = container_of(w, struct usbpd, sm_work.work); + struct usbpd *pd = container_of(w, struct usbpd, sm_work); union power_supply_propval val = {0}; int ret; enum usbpd_control_msg_type ctrl_recvd = 0; @@ -1206,6 +1224,8 @@ static void usbpd_sm(struct work_struct *w) usbpd_dbg(&pd->dev, "handle state %s\n", usbpd_state_strings[pd->current_state]); + hrtimer_cancel(&pd->timer); + if (pd->rx_msg_len) data_recvd = pd->rx_msg_type; else @@ -1251,12 +1271,18 @@ static void usbpd_sm(struct work_struct *w) reset_vdm_state(pd); + if (pd->current_state == PE_ERROR_RECOVERY) + /* forced disconnect, wait before resetting to DRP */ + usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC, + (ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC); + /* Set CC back to DRP toggle */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); pd->current_state = PE_UNKNOWN; + return; } @@ -1327,8 +1353,8 @@ static void usbpd_sm(struct work_struct *w) break; } - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SRC_CAP_TIME)); + hrtimer_start(&pd->timer, ms_to_ktime(SRC_CAP_TIME), + HRTIMER_MODE_REL); break; } @@ -1343,8 +1369,8 @@ static void usbpd_sm(struct work_struct *w) /* wait for REQUEST */ pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(SENDER_RESPONSE_TIME * 3)); + hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME), + HRTIMER_MODE_REL); break; case PE_SRC_SEND_CAPABILITIES_WAIT: @@ -1356,17 +1382,6 @@ static void usbpd_sm(struct work_struct *w) } break; - case PE_SRC_TRANSITION_SUPPLY: - ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); - if (ret) { - usbpd_err(&pd->dev, "Error sending PS_RDY\n"); - usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); - break; - } - - usbpd_set_state(pd, PE_SRC_READY); - break; - case PE_SRC_READY: if (ctrl_recvd == MSG_GET_SOURCE_CAP) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, @@ -1405,7 +1420,7 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); break; } else { if (data_recvd == MSG_VDM) @@ -1420,7 +1435,8 @@ static void usbpd_sm(struct work_struct *w) pd->in_explicit_contract = false; reset_vdm_state(pd); - msleep(PS_HARD_RESET_TIME); + usleep_range(PS_HARD_RESET_TIME * USEC_PER_MSEC, + (PS_HARD_RESET_TIME + 5) * USEC_PER_MSEC); usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); break; @@ -1431,7 +1447,7 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_TYPE, &val); if (val.intval == POWER_SUPPLY_TYPEC_NONE) { pd->typec_mode = POWER_SUPPLY_TYPEC_NONE; - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); } break; } @@ -1559,8 +1575,8 @@ static void usbpd_sm(struct work_struct *w) pd->current_pr = PR_SRC; pd_phy_update_roles(pd->current_dr, pd->current_pr); - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(PS_SOURCE_OFF)); + hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF), + HRTIMER_MODE_REL); break; } else { if (data_recvd == MSG_VDM) @@ -1661,8 +1677,8 @@ static void usbpd_sm(struct work_struct *w) } pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON; - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(PS_SOURCE_ON)); + hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_ON), + HRTIMER_MODE_REL); break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: @@ -1690,8 +1706,8 @@ static void usbpd_sm(struct work_struct *w) pd->current_pr = PR_SRC; pd_phy_update_roles(pd->current_dr, pd->current_pr); - queue_delayed_work(pd->wq, &pd->sm_work, - msecs_to_jiffies(PS_SOURCE_OFF)); + hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF), + HRTIMER_MODE_REL); break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: @@ -1821,7 +1837,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) switch (typec_mode) { /* Disconnect */ case POWER_SUPPLY_TYPEC_NONE: - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); break; /* Sink states */ @@ -1831,7 +1847,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Source connected\n"); if (pd->current_pr != PR_SINK) { pd->current_pr = PR_SINK; - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); } break; @@ -1841,7 +1857,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) usbpd_info(&pd->dev, "Type-C Sink connected\n"); if (pd->current_pr != PR_SRC) { pd->current_pr = PR_SRC; - queue_delayed_work(pd->wq, &pd->sm_work, 0); + queue_work(pd->wq, &pd->sm_work); } break; @@ -2284,7 +2300,9 @@ struct usbpd *usbpd_create(struct device *parent) ret = -ENOMEM; goto del_pd; } - INIT_DELAYED_WORK(&pd->sm_work, usbpd_sm); + INIT_WORK(&pd->sm_work, usbpd_sm); + hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pd->timer.function = pd_timeout; pd->usb_psy = power_supply_get_by_name("usb"); if (!pd->usb_psy) { diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index 70bb916d863f..8cf294306efd 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -58,6 +58,12 @@ #define USB_PDPHY_RX_ACKNOWLEDGE 0x4B #define RX_BUFFER_TOKEN BIT(0) +#define USB_PDPHY_BIST_MODE 0x4E +#define BIST_MODE_MASK 0xF +#define BIST_ENABLE BIT(7) +#define PD_MSG_BIST 0x3 +#define PD_BIST_TEST_DATA_MODE 0x8 + #define USB_PDPHY_TX_BUFFER_HDR 0x60 #define USB_PDPHY_TX_BUFFER_DATA 0x62 @@ -95,6 +101,7 @@ struct usb_pdphy { bool is_opened; int tx_status; u8 frame_filter_val; + bool in_test_data_mode; enum data_role data_role; enum power_role power_role; @@ -233,7 +240,8 @@ void pdphy_enable_irq(struct usb_pdphy *pdphy, bool enable) enable_irq(pdphy->sig_tx_irq); enable_irq(pdphy->sig_rx_irq); enable_irq(pdphy->msg_tx_irq); - enable_irq(pdphy->msg_rx_irq); + if (!pdphy->in_test_data_mode) + enable_irq(pdphy->msg_rx_irq); enable_irq(pdphy->msg_tx_failed_irq); enable_irq(pdphy->msg_tx_discarded_irq); enable_irq(pdphy->msg_rx_discarded_irq); @@ -243,7 +251,8 @@ void pdphy_enable_irq(struct usb_pdphy *pdphy, bool enable) disable_irq(pdphy->sig_tx_irq); disable_irq(pdphy->sig_rx_irq); disable_irq(pdphy->msg_tx_irq); - disable_irq(pdphy->msg_rx_irq); + if (!pdphy->in_test_data_mode) + disable_irq(pdphy->msg_rx_irq); disable_irq(pdphy->msg_tx_failed_irq); disable_irq(pdphy->msg_tx_discarded_irq); disable_irq(pdphy->msg_rx_discarded_irq); @@ -523,6 +532,9 @@ void pd_phy_close(void) wake_up_all(&pdphy->tx_waitq); + pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0); + pdphy->in_test_data_mode = false; + ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, 0); if (ret) return; @@ -600,6 +612,9 @@ static irqreturn_t pdphy_sig_tx_irq_thread(int irq, void *data) { struct usb_pdphy *pdphy = data; + /* in case of exit from BIST Carrier Mode 2, clear BIST_MODE */ + pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0); + pdphy->sig_tx_cnt++; pdphy->tx_status = 0; wake_up(&pdphy->tx_waitq); @@ -607,10 +622,24 @@ static irqreturn_t pdphy_sig_tx_irq_thread(int irq, void *data) return IRQ_HANDLED; } +static int pd_phy_bist_mode(u8 bist_mode) +{ + struct usb_pdphy *pdphy = __pdphy; + + dev_dbg(pdphy->dev, "%s: enter BIST mode %d\n", __func__, bist_mode); + + pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0); + + udelay(5); + + return pdphy_masked_write(pdphy, USB_PDPHY_BIST_MODE, + BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE); +} + static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) { u8 size, rx_status, frame_type; - u8 *buf = NULL; + u8 buf[32]; int ret; struct usb_pdphy *pdphy = data; @@ -620,9 +649,8 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) if (ret) goto done; - if (!size) { - dev_err(pdphy->dev, "%s: incorrect size 1byte\n", - __func__); + if (!size || size > 31) { + dev_err(pdphy->dev, "%s: invalid size %d\n", __func__, size); goto done; } @@ -637,25 +665,33 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) goto done; } - buf = kmalloc(size + 1, GFP_KERNEL); - if (!buf) - goto done; - ret = pdphy_reg_read(pdphy, buf, USB_PDPHY_RX_BUFFER, size + 1); if (ret) goto done; - /* ack to change ownership of rx buffer back to PDPHY RX HW */ - pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0); + if (((buf[0] & 0xf) == PD_MSG_BIST) && size >= 5) { /* BIST */ + u8 mode = buf[5] >> 4; /* [31:28] of 1st data object */ + + pd_phy_bist_mode(mode); + pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0); + + if (mode == PD_BIST_TEST_DATA_MODE) { + pdphy->in_test_data_mode = true; + disable_irq_nosync(irq); + } + goto done; + } if (pdphy->msg_rx_cb) pdphy->msg_rx_cb(pdphy->usbpd, frame_type, buf, size + 1); + /* ack to change ownership of rx buffer back to PDPHY RX HW */ + pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0); + print_hex_dump_debug("rx msg:", DUMP_PREFIX_NONE, 32, 4, buf, size + 1, false); pdphy->rx_bytes += size + 1; done: - kfree(buf); return IRQ_HANDLED; } diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 794bc2e33695..529e4ea76a96 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -465,6 +465,9 @@ static int msm_ssphy_qmp_set_suspend(struct usb_phy *uphy, int suspend) else msm_ssusb_qmp_enable_autonomous(phy, 1); + /* Make sure above write completed with PHY */ + wmb(); + clk_disable_unprepare(phy->cfg_ahb_clk); clk_disable_unprepare(phy->aux_clk); clk_disable_unprepare(phy->pipe_clk); @@ -494,6 +497,10 @@ static int msm_ssphy_qmp_set_suspend(struct usb_phy *uphy, int suspend) } else { msm_ssusb_qmp_enable_autonomous(phy, 0); } + + /* Make sure that above write completed with PHY */ + wmb(); + phy->in_suspend = false; dev_dbg(uphy->dev, "QMP PHY is resumed\n"); } diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 61e99f47f02f..4724f4378e23 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -277,6 +277,8 @@ struct mdss_data_type { struct regulator *fs; struct regulator *venus; struct regulator *vdd_cx; + u32 vdd_cx_min_uv; + u32 vdd_cx_max_uv; bool batfet_required; struct regulator *batfet; bool en_svs_high; diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 0ac5ef4f750c..e0dd3f5d5635 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -42,43 +42,6 @@ #define VDDA_UA_ON_LOAD 100000 /* uA units */ #define VDDA_UA_OFF_LOAD 100 /* uA units */ -static char edid_buf1[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x22, 0xf0, 0x52, 0x29, 0x01, 0x01, 0x01, 0x01, - 0x16, 0x16, 0x01, 0x03, 0x80, 0x30, 0x1b, 0x78, - 0x2e, 0xee, 0x95, 0xa3, 0x54, 0x4c, 0x99, 0x26, - 0x0f, 0x50, 0x54, 0xa1, 0x08, 0x00, 0xd1, 0xc0, - 0x81, 0xc0, 0xa9, 0xc0, 0xb3, 0x00, 0x95, 0x00, - 0x81, 0x40, 0x81, 0x80, 0x01, 0x01, 0x02, 0x3a, - 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x45, 0x00, 0xdb, 0x0b, 0x11, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, 0x4c, 0x18, - 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x48, - 0x50, 0x20, 0x5a, 0x52, 0x32, 0x32, 0x34, 0x30, - 0x77, 0x0a, 0x20, 0x20, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x43, 0x4e, 0x34, 0x32, 0x32, 0x32, 0x30, - 0x30, 0x33, 0x46, 0x0a, 0x20, 0x20, 0x01, 0xb1, - - 0x02, 0x03, 0x17, 0xb1, 0x4c, 0x90, 0x1f, 0x05, - 0x14, 0x04, 0x13, 0x03, 0x02, 0x07, 0x06, 0x12, - 0x01, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x02, - 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, - 0x2c, 0x45, 0x00, 0xdb, 0x0b, 0x11, 0x00, 0x00, - 0x1e, 0x02, 0x3a, 0x80, 0xd0, 0x72, 0x38, 0x2d, - 0x40, 0x10, 0x2c, 0x45, 0x80, 0xdb, 0x0b, 0x11, - 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0x72, 0x51, - 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0xdb, - 0x0b, 0x11, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, - 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28, 0x55, - 0x40, 0xdb, 0x0b, 0x11, 0x00, 0x00, 0x1e, 0x8c, - 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, - 0x3e, 0x96, 0x00, 0xdb, 0x0b, 0x11, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b -}; - - static void mdss_dp_put_dt_clk_data(struct device *dev, struct dss_module_power *module_power) { @@ -945,9 +908,6 @@ int mdss_dp_on(struct mdss_panel_data *pdata) pr_err("Unabled to start core clocks\n"); return ret; } - mdss_dp_phy_reset(&dp_drv->ctrl_io); - mdss_dp_aux_reset(&dp_drv->ctrl_io); - mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); mdss_dp_hpd_configure(&dp_drv->ctrl_io, true); orientation = usbpd_get_plug_orientation(dp_drv->pd); @@ -971,11 +931,6 @@ int mdss_dp_on(struct mdss_panel_data *pdata) if (dp_drv->new_vic && (dp_drv->new_vic != dp_drv->vic)) dp_init_panel_info(dp_drv, dp_drv->new_vic); - mdss_dp_phy_aux_setup(&dp_drv->phy_io); - - mdss_dp_irq_enable(dp_drv); - pr_debug("irq enabled\n"); - mdss_dp_dpcd_cap_read(dp_drv); dp_drv->link_rate = mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info, dp_drv->dpcd.max_lane_count); @@ -1143,9 +1098,10 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata) return -ENODEV; } - /* Use the existing EDID buffer for 1080p */ - memcpy(edid_init_data.buf, edid_buf1, sizeof(edid_buf1)); dp_drv->panel_data.panel_info.edid_data = edid_data; + /* initialize EDID buffer pointers */ + dp_drv->edid_buf = edid_init_data.buf; + dp_drv->edid_buf_size = edid_init_data.buf_size; return 0; } @@ -1189,17 +1145,30 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io), mdss_dp_get_phy_hw_version(&dp_drv->phy_io)); + mdss_dp_phy_aux_setup(&dp_drv->phy_io); + + mdss_dp_irq_enable(dp_drv); + pr_debug("irq enabled\n"); + mdss_dp_dpcd_cap_read(dp_drv); + + ret = mdss_dp_edid_read(dp_drv); + if (ret) + goto edid_error; + + pr_debug("edid_read success. buf_size=%d\n", + dp_drv->edid_buf_size); + ret = hdmi_edid_parser(dp_drv->panel_data.panel_info.edid_data); if (ret) { DEV_ERR("%s: edid parse failed\n", __func__); - goto edid_parser_error; + goto edid_error; } mdss_dp_send_cable_notification(dp_drv, true); return ret; -edid_parser_error: +edid_error: mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); clk_error: mdss_dp_regulator_ctrl(dp_drv, false); @@ -1422,7 +1391,7 @@ static void mdss_dp_event_work(struct work_struct *work) switch (todo) { case EV_EDID_READ: - mdss_dp_edid_read(dp, 0); + mdss_dp_edid_read(dp); break; case EV_DPCD_CAP_READ: mdss_dp_dpcd_cap_read(dp); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 03646cd7cc65..4a80c25dd157 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -106,15 +106,6 @@ #define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2) -struct edp_cmd { - char read; /* 1 == read, 0 == write */ - char i2c; /* 1 == i2c cmd, 0 == native cmd */ - u32 addr; /* 20 bits */ - char *datap; - int len; /* len to be tx OR len to be rx for read */ - char next; /* next command */ -}; - struct edp_buf { char *start; /* buffer start addr */ char *end; /* buffer end addr */ @@ -378,6 +369,8 @@ struct mdss_dp_drv_pdata { char train_link_rate; /* X 27000000 for real rate */ char train_lane_cnt; + u8 *edid_buf; + u32 edid_buf_size; struct edp_edid edid; struct dpcd_cap dpcd; @@ -464,7 +457,7 @@ void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp); -void mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp, int block); +int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp); int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp); void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *dp, u32 isr); void dp_aux_native_handler(struct mdss_dp_drv_pdata *dp, u32 isr); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 7b14a7efb9dc..7e9510cf08c7 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -228,6 +228,11 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, return ret; } +int dp_aux_write(void *ep, struct edp_cmd *cmd) +{ + return dp_aux_write_cmds(ep, cmd); +} + static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { @@ -275,12 +280,20 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, else ret = ep->aux_error_num; + if (cmds->out_buf) + memcpy(cmds->out_buf, rp->data, cmds->len); + ep->aux_cmd_busy = 0; mutex_unlock(&ep->aux_mutex); return ret; } +int dp_aux_read(void *ep, struct edp_cmd *cmds) +{ + return dp_aux_read_cmds(ep, cmds); +} + void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { @@ -665,7 +678,7 @@ static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) char data = 0; for (cnt = 5; cnt; cnt--) { - ret = dp_aux_write_buf(ep, 0x50, &data, 1, 1); + ret = dp_aux_write_buf(ep, EDID_START_ADDRESS, &data, 1, 1); pr_debug("ret=%d\n", ret); if (ret >= 0) break; @@ -680,43 +693,85 @@ static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) return 0; } -static int dp_sink_edid_read(struct mdss_dp_drv_pdata *ep, int block) +int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) { struct edp_buf *rp; int cnt, rlen; int ret = 0; + int blk_num = 0; - ret = dp_aux_chan_ready(ep); + ret = dp_aux_chan_ready(dp); if (ret) { pr_err("aux chan NOT ready\n"); return ret; } for (cnt = 5; cnt; cnt--) { - rlen = dp_aux_read_buf(ep, 0x50, 128, 1); + rlen = dp_aux_read_buf + (dp, EDID_START_ADDRESS, EDID_BLOCK_SIZE, 1); if (rlen > 0) { - pr_debug("rlen=%d\n", rlen); + pr_debug("cnt=%d, block=%d, rlen=%d\n", + cnt, blk_num, rlen); - rp = &ep->rxp; + rp = &dp->rxp; if (!dp_edid_buf_error(rp->data, rp->len)) break; } } - if (cnt <= 0) { - pr_err("Failed\n"); + if ((cnt <= 0) && (rlen != EDID_BLOCK_SIZE)) { + pr_err("Read failed. rlen=%d\n", rlen); return -EINVAL; } - dp_extract_edid_manufacturer(&ep->edid, rp->data); - dp_extract_edid_product(&ep->edid, rp->data); - dp_extract_edid_version(&ep->edid, rp->data); - dp_extract_edid_ext_block_cnt(&ep->edid, rp->data); - dp_extract_edid_video_support(&ep->edid, rp->data); - dp_extract_edid_feature(&ep->edid, rp->data); - dp_extract_edid_detailed_timing_description(&ep->edid, rp->data); + rp = &dp->rxp; + + dp_extract_edid_manufacturer(&dp->edid, rp->data); + dp_extract_edid_product(&dp->edid, rp->data); + dp_extract_edid_version(&dp->edid, rp->data); + dp_extract_edid_ext_block_cnt(&dp->edid, rp->data); + dp_extract_edid_video_support(&dp->edid, rp->data); + dp_extract_edid_feature(&dp->edid, rp->data); + dp_extract_edid_detailed_timing_description(&dp->edid, rp->data); + /* for the first block initialize the edid buffer size */ + dp->edid_buf_size = 0; + + pr_debug("edid extension = %d\n", + dp->edid.ext_block_cnt); + + memcpy(dp->edid_buf, rp->data, EDID_BLOCK_SIZE); + dp->edid_buf_size += EDID_BLOCK_SIZE; - return 128; + if (!dp->edid.ext_block_cnt) + return 0; + + for (blk_num = 1; blk_num <= dp->edid.ext_block_cnt; + blk_num++) { + for (cnt = 5; cnt; cnt--) { + rlen = dp_aux_read_buf + (dp, EDID_START_ADDRESS + + (blk_num * EDID_BLOCK_SIZE), + EDID_BLOCK_SIZE, 1); + if (rlen > 0) { + pr_debug("cnt=%d, blk_num=%d, rlen=%d\n", + cnt, blk_num, rlen); + rp = &dp->rxp; + if (!dp_edid_buf_error(rp->data, rp->len)) + break; + } + } + + if ((cnt <= 0) && (rlen != EDID_BLOCK_SIZE)) { + pr_err("Read failed. rlen=%d\n", rlen); + return -EINVAL; + } + + memcpy(dp->edid_buf + (blk_num * EDID_BLOCK_SIZE), + rp->data, EDID_BLOCK_SIZE); + dp->edid_buf_size += EDID_BLOCK_SIZE; + } + + return 0; } static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, @@ -1357,11 +1412,6 @@ void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep) } -void mdss_dp_edid_read(struct mdss_dp_drv_pdata *ep, int block) -{ - dp_sink_edid_read(ep, block); -} - int mdss_dp_link_train(struct mdss_dp_drv_pdata *ep) { int ret; diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 8ef00dd7248e..a2649b8c1611 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -86,6 +86,33 @@ #define TXn_TX_DRV_LVL 0x001C #define TCSR_USB3_DP_PHYMODE 0x48 +#define EDID_START_ADDRESS 0x50 + +/* DP HDCP 1.3 registers */ +#define DP_HDCP_CTRL (0x0A0) +#define DP_HDCP_STATUS (0x0A4) +#define DP_HDCP_SW_UPPER_AKSV (0x298) +#define DP_HDCP_SW_LOWER_AKSV (0x29C) +#define DP_HDCP_ENTROPY_CTRL0 (0x750) +#define DP_HDCP_ENTROPY_CTRL1 (0x75C) +#define DP_HDCP_SHA_STATUS (0x0C8) +#define DP_HDCP_RCVPORT_DATA2_0 (0x0B0) +#define DP_HDCP_RCVPORT_DATA3 (0x2A4) +#define DP_HDCP_RCVPORT_DATA4 (0x2A8) +#define DP_HDCP_RCVPORT_DATA5 (0x0C0) +#define DP_HDCP_RCVPORT_DATA6 (0x0C4) + +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL (0x024) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0 (0x004) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1 (0x008) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7 (0x00C) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8 (0x010) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9 (0x014) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C) +#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020) + +#define DP_INTERRUPT_STATUS_2 (0x024) struct lane_mapping { char lane0; @@ -94,6 +121,18 @@ struct lane_mapping { char lane3; }; +struct edp_cmd { + char read; /* 1 == read, 0 == write */ + char i2c; /* 1 == i2c cmd, 0 == native cmd */ + u32 addr; /* 20 bits */ + char *datap; + char *out_buf; + int len; /* len to be tx OR len to be rx for read */ + char next; /* next command */ +}; + +int dp_aux_read(void *ep, struct edp_cmd *cmds); +int dp_aux_write(void *ep, struct edp_cmd *cmd); void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io); u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c index 6256c9a99877..dad089e74934 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c @@ -15,9 +15,11 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/iopoll.h> #include <soc/qcom/scm.h> #include <linux/hdcp_qseecom.h> #include "mdss_hdmi_hdcp.h" +#include "mdss_dp_util.h" #include "video/msm_hdmi_hdcp_mgr.h" #define HDCP_STATE_NAME (hdcp_state_name(hdcp_ctrl->hdcp_state)) @@ -36,15 +38,188 @@ #define HDCP_REG_ENABLE 0x01 #define HDCP_REG_DISABLE 0x00 -#define HDCP_INT_CLR (BIT(1) | BIT(5) | BIT(7) | BIT(9) | BIT(13)) +#define HDCP_INT_CLR (isr->auth_success_ack | isr->auth_fail_ack | \ + isr->auth_fail_info_ack | isr->tx_req_ack | \ + isr->encryption_ready_ack | \ + isr->encryption_not_ready | isr->tx_req_done_ack) + +#define HDCP_INT_EN (isr->auth_success_mask | isr->auth_fail_mask | \ + isr->encryption_ready_mask | \ + isr->encryption_not_ready_mask) + +#define HDCP_POLL_SLEEP_US (20 * 1000) +#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 1000) + +#define reg_set_data(x) \ + (hdcp_ctrl->init_data.sec_access ? reg_set->sec_data##x : \ + reg_set->data##x) + +struct hdcp_sink_addr { + char *name; + u32 addr; + u32 len; +}; struct hdmi_hdcp_reg_data { u32 reg_id; - u32 off; - char *name; - u32 reg_val; + struct hdcp_sink_addr *sink; +}; + +struct hdcp_sink_addr_map { + /* addresses to read from sink */ + struct hdcp_sink_addr bcaps; + struct hdcp_sink_addr bksv; + struct hdcp_sink_addr r0; + struct hdcp_sink_addr bstatus; + struct hdcp_sink_addr ksv_fifo; + struct hdcp_sink_addr v_h0; + struct hdcp_sink_addr v_h1; + struct hdcp_sink_addr v_h2; + struct hdcp_sink_addr v_h3; + struct hdcp_sink_addr v_h4; + + /* addresses to write to sink */ + struct hdcp_sink_addr an; + struct hdcp_sink_addr aksv; + struct hdcp_sink_addr rep; +}; + +struct hdcp_int_set { + /* interrupt register */ + u32 int_reg; + + /* interrupt enable/disable masks */ + u32 auth_success_mask; + u32 auth_fail_mask; + u32 encryption_ready_mask; + u32 encryption_not_ready_mask; + u32 tx_req_mask; + u32 tx_req_done_mask; + + /* interrupt acknowledgment */ + u32 auth_success_ack; + u32 auth_fail_ack; + u32 auth_fail_info_ack; + u32 encryption_ready_ack; + u32 encryption_not_ready_ack; + u32 tx_req_ack; + u32 tx_req_done_ack; + + /* interrupt status */ + u32 auth_success_int; + u32 auth_fail_int; + u32 encryption_ready; + u32 encryption_not_ready; + u32 tx_req_int; + u32 tx_req_done_int; +}; + +struct hdcp_reg_set { + u32 status; + u32 keys_offset; + u32 r0_offset; + u32 v_offset; + u32 ctrl; + u32 aksv_lsb; + u32 aksv_msb; + u32 entropy_ctrl0; + u32 entropy_ctrl1; + u32 sha_ctrl; + u32 sec_sha_ctrl; + u32 sha_status; + + u32 data0; + u32 data1; + u32 data2_0; + u32 data3; + u32 data4; + u32 data5; + u32 data6; + u32 data7; + u32 data8; + u32 data9; + u32 data10; + u32 data11; + u32 data12; + + u32 sec_data0; + u32 sec_data1; + u32 sec_data7; + u32 sec_data8; + u32 sec_data9; + u32 sec_data10; + u32 sec_data11; + u32 sec_data12; + + u32 reset; }; +#define HDCP_REG_SET_CLIENT_HDMI \ + {HDMI_HDCP_LINK0_STATUS, 28, 24, 20, HDMI_HDCP_CTRL, \ + HDMI_HDCP_SW_LOWER_AKSV, HDMI_HDCP_SW_UPPER_AKSV, \ + HDMI_HDCP_ENTROPY_CTRL0, HDMI_HDCP_ENTROPY_CTRL1, \ + HDMI_HDCP_SHA_CTRL, HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, \ + HDMI_HDCP_SHA_STATUS, HDMI_HDCP_RCVPORT_DATA0, \ + HDMI_HDCP_RCVPORT_DATA1, HDMI_HDCP_RCVPORT_DATA2_0, \ + HDMI_HDCP_RCVPORT_DATA3, HDMI_HDCP_RCVPORT_DATA4, \ + HDMI_HDCP_RCVPORT_DATA5, HDMI_HDCP_RCVPORT_DATA6, \ + HDMI_HDCP_RCVPORT_DATA7, HDMI_HDCP_RCVPORT_DATA8, \ + HDMI_HDCP_RCVPORT_DATA9, HDMI_HDCP_RCVPORT_DATA10, \ + HDMI_HDCP_RCVPORT_DATA11, HDMI_HDCP_RCVPORT_DATA12, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \ + HDMI_HDCP_RESET} + +#define HDCP_REG_SET_CLIENT_DP \ + {DP_HDCP_STATUS, 16, 14, 13, DP_HDCP_CTRL, \ + DP_HDCP_SW_LOWER_AKSV, DP_HDCP_SW_UPPER_AKSV, \ + DP_HDCP_ENTROPY_CTRL0, DP_HDCP_ENTROPY_CTRL1, \ + 0, HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL, \ + DP_HDCP_SHA_STATUS, 0, 0, DP_HDCP_RCVPORT_DATA2_0, \ + DP_HDCP_RCVPORT_DATA3, DP_HDCP_RCVPORT_DATA4, \ + DP_HDCP_RCVPORT_DATA5, DP_HDCP_RCVPORT_DATA6, \ + 0, 0, 0, 0, 0, 0, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, 0} + +#define HDCP_HDMI_SINK_ADDR_MAP \ + {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \ + {"bstatus", 0x41, 2}, {"ksv-fifo", 0x43, 0}, {"v_h0", 0x20, 4}, \ + {"v_h1", 0x24, 4}, {"v_h2", 0x28, 4}, {"v_h3", 0x2c, 4}, \ + {"v_h4", 0x30, 4}, {"an", 0x16, 8}, {"aksv", 0x10, 5}, \ + {"repater", 0x00, 0} } + +#define HDCP_DP_SINK_ADDR_MAP \ + {{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \ + {"bstatus", 0x6802A, 2}, {"ksv-fifo", 0x6802A, 0}, \ + {"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, {"v_h2", 0x6801C, 4}, \ + {"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, {"an", 0x6800C, 8}, \ + {"aksv", 0x68007, 5}, {"repater", 0x68028, 1} } + +#define HDCP_HDMI_INT_SET \ + {HDMI_HDCP_INT_CTRL, \ + BIT(2), BIT(6), 0, 0, 0, 0, \ + BIT(1), BIT(5), BIT(7), 0, 0, 0, 0, \ + BIT(0), BIT(4), 0, 0, 0, 0} + +#define HDCP_DP_INT_SET \ + {DP_INTERRUPT_STATUS_2, \ + BIT(17), BIT(20), BIT(24), BIT(27), 0, 0, \ + BIT(16), BIT(19), BIT(21), BIT(23), BIT(26), 0, 0, \ + BIT(15), BIT(18), BIT(22), BIT(25), 0, 0} + struct hdmi_hdcp_ctrl { u32 auth_retries; u32 tp_msgid; @@ -57,7 +232,9 @@ struct hdmi_hdcp_ctrl { struct completion r0_checked; struct hdmi_hdcp_init_data init_data; struct hdmi_hdcp_ops *ops; - bool hdmi_tx_ver_4; + struct hdcp_reg_set reg_set; + struct hdcp_int_set int_set; + struct hdcp_sink_addr_map sink_addr; }; const char *hdcp_state_name(enum hdmi_hdcp_state hdcp_state) @@ -256,6 +433,7 @@ static int hdmi_hdcp_load_keys(void *input) struct dss_io_data *io; struct dss_io_data *qfprom_io; struct hdmi_hdcp_ctrl *hdcp_ctrl = input; + struct hdcp_reg_set *reg_set; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io || !hdcp_ctrl->init_data.qfprom_io) { @@ -274,6 +452,7 @@ static int hdmi_hdcp_load_keys(void *input) io = hdcp_ctrl->init_data.core_io; qfprom_io = hdcp_ctrl->init_data.qfprom_io; + reg_set = &hdcp_ctrl->reg_set; /* On compatible hardware, use SW keys */ reg_val = DSS_REG_R(qfprom_io, SEC_CTRL_HW_VERSION); @@ -297,7 +476,7 @@ static int hdmi_hdcp_load_keys(void *input) ksv_lsb_addr = HDCP_KSV_LSB; ksv_msb_addr = HDCP_KSV_MSB; - if (hdcp_ctrl->hdmi_tx_ver_4) { + if (hdcp_ctrl->init_data.sec_access) { ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET; ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET; } @@ -322,27 +501,121 @@ static int hdmi_hdcp_load_keys(void *input) goto end; } - DSS_REG_W(io, HDMI_HDCP_SW_LOWER_AKSV, aksv_lsb); - DSS_REG_W(io, HDMI_HDCP_SW_UPPER_AKSV, aksv_msb); + DSS_REG_W(io, reg_set->aksv_lsb, aksv_lsb); + DSS_REG_W(io, reg_set->aksv_msb, aksv_msb); /* Setup seed values for random number An */ - DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF); - DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE); - - /* Disable the RngCipher state */ - DSS_REG_W(io, HDMI_HDCP_DEBUG_CTRL, - DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL) & ~(BIT(2))); + DSS_REG_W(io, reg_set->entropy_ctrl0, 0xB1FFB0FF); + DSS_REG_W(io, reg_set->entropy_ctrl1, 0xF00DFACE); /* make sure hw is programmed */ wmb(); - DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0)); + /* enable hdcp engine */ + DSS_REG_W(io, reg_set->ctrl, 0x1); hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING; end: return rc; } +static int hdmi_hdcp_read(struct hdmi_hdcp_ctrl *hdcp_ctrl, + struct hdcp_sink_addr *sink, + u8 *buf, bool realign) +{ + u32 rc = 0; + struct hdmi_tx_ddc_data ddc_data; + + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) { + reset_hdcp_ddc_failures(hdcp_ctrl); + + memset(&ddc_data, 0, sizeof(ddc_data)); + ddc_data.dev_addr = 0x74; + ddc_data.offset = sink->addr; + ddc_data.data_buf = buf; + ddc_data.data_len = sink->len; + ddc_data.request_len = sink->len; + ddc_data.retry = 5; + ddc_data.what = sink->name; + ddc_data.retry_align = realign; + + hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; + + rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); + if (rc) + DEV_ERR("%s: %s: %s read failed\n", __func__, + HDCP_STATE_NAME, sink->name); + } else if (IS_ENABLED(CONFIG_FB_MSM_MDSS_DP_PANEL) && + hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) { + struct edp_cmd cmd = {0}; + + cmd.read = 1; + cmd.addr = sink->addr; + cmd.out_buf = buf; + cmd.len = sink->len; + + rc = dp_aux_read(hdcp_ctrl->init_data.dp_data, &cmd); + if (rc) + DEV_ERR("%s: %s: %s read failed\n", __func__, + HDCP_STATE_NAME, sink->name); + } + + return rc; +} + +static int hdmi_hdcp_write(struct hdmi_hdcp_ctrl *hdcp_ctrl, + u32 offset, u32 len, u8 *buf, char *name) +{ + int rc = 0; + struct hdmi_tx_ddc_data ddc_data; + + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) { + memset(&ddc_data, 0, sizeof(ddc_data)); + + ddc_data.dev_addr = 0x74; + ddc_data.offset = offset; + ddc_data.data_buf = buf; + ddc_data.data_len = len; + ddc_data.what = name; + hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; + + rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl); + if (rc) + DEV_ERR("%s: %s: %s write failed\n", __func__, + HDCP_STATE_NAME, name); + } else if (IS_ENABLED(CONFIG_FB_MSM_MDSS_DP_PANEL) && + hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) { + struct edp_cmd cmd = {0}; + + cmd.addr = offset; + cmd.len = len; + cmd.datap = buf; + + rc = dp_aux_write(hdcp_ctrl->init_data.dp_data, &cmd); + if (rc) + DEV_ERR("%s: %s: %s read failed\n", __func__, + HDCP_STATE_NAME, name); + } + + return rc; +} + +static void hdmi_hdcp_enable_interrupts(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + u32 intr_reg; + struct dss_io_data *io; + struct hdcp_int_set *isr; + + io = hdcp_ctrl->init_data.core_io; + isr = &hdcp_ctrl->int_set; + + intr_reg = DSS_REG_R(io, isr->int_reg); + + intr_reg |= HDCP_INT_CLR | HDCP_INT_EN; + + DSS_REG_W(io, isr->int_reg, intr_reg); +} + static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; @@ -353,13 +626,12 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) bool is_match; struct dss_io_data *io; struct dss_io_data *hdcp_io; + struct hdcp_reg_set *reg_set; u8 aksv[5], *bksv = NULL; u8 an[8]; u8 bcaps; - struct hdmi_tx_ddc_data ddc_data; - u32 link0_status, an_ready, keys_state; + u32 link0_status; u8 buf[0xFF]; - struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG]; u32 phy_addr; u32 ret = 0; @@ -376,6 +648,7 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) bksv = hdcp_ctrl->current_tp.bksv; io = hdcp_ctrl->init_data.core_io; hdcp_io = hdcp_ctrl->init_data.hdcp_io; + reg_set = &hdcp_ctrl->reg_set; if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) { DEV_ERR("%s: %s: invalid state. returning\n", __func__, @@ -384,32 +657,14 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) goto error; } - /* Clear any DDC failures from previous tries */ - reset_hdcp_ddc_failures(hdcp_ctrl); - - /* - * Read BCAPS - * We need to first try to read an HDCP register on the sink to see if - * the sink is ready for HDCP authentication - */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x40; - ddc_data.data_buf = &bcaps; - ddc_data.data_len = 1; - ddc_data.request_len = 1; - ddc_data.retry = 5; - ddc_data.what = "Bcaps"; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: BCAPS read failed\n", __func__, - HDCP_STATE_NAME); + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.bcaps, + &bcaps, false); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading bcaps\n", __func__); goto error; } - DEV_DBG("%s: %s: BCAPS=%02x\n", __func__, HDCP_STATE_NAME, bcaps); + + hdmi_hdcp_enable_interrupts(hdcp_ctrl); /* receiver (0), repeater (1) */ hdcp_ctrl->current_tp.ds_type = @@ -419,7 +674,7 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) if (hdcp_ctrl->tz_hdcp) { memset(scm_buf, 0x00, sizeof(scm_buf)); - scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA12; + scm_buf[0].addr = phy_addr + reg_set->data12; scm_buf[0].val = bcaps; ret = hdcp_scm_call(scm_buf, &resp); @@ -429,30 +684,19 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { - DSS_REG_W(hdcp_io, HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, - bcaps); + } else if (hdcp_ctrl->init_data.sec_access) { + DSS_REG_W(hdcp_io, reg_set->sec_data12, bcaps); } else { - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps); + DSS_REG_W(io, reg_set->data12, bcaps); } /* Wait for HDCP keys to be checked and validated */ - timeout_count = 100; - keys_state = (link0_status >> 28) & 0x7; - while ((keys_state != HDCP_KEYS_STATE_VALID) && - --timeout_count) { - link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); - keys_state = (link0_status >> 28) & 0x7; - DEV_DBG("%s: %s: Keys not ready(%d). s=%d\n, l0=%0x08x", - __func__, HDCP_STATE_NAME, timeout_count, - keys_state, link0_status); - msleep(20); - } - - if (!timeout_count) { - DEV_ERR("%s: %s: Invalid Keys State: %d\n", __func__, - HDCP_STATE_NAME, keys_state); - rc = -EINVAL; + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + ((link0_status >> reg_set->keys_offset) & 0x7) + == HDCP_KEYS_STATE_VALID, + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: key not ready\n", __func__); goto error; } @@ -460,39 +704,39 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) * 1.1_Features turned off by default. * No need to write AInfo since 1.1_Features is disabled. */ - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA4, 0); + DSS_REG_W(io, reg_set->data4, 0); /* Wait for An0 and An1 bit to be ready */ - timeout_count = 100; - do { - link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); - an_ready = (link0_status & BIT(8)) && (link0_status & BIT(9)); - if (!an_ready) { - DEV_DBG("%s: %s: An not ready(%d). l0_status=0x%08x\n", - __func__, HDCP_STATE_NAME, timeout_count, - link0_status); - msleep(20); - } - } while (!an_ready && --timeout_count); - - if (!timeout_count) { - rc = -ETIMEDOUT; - DEV_ERR("%s: %s: timedout, An0=%ld, An1=%ld\n", __func__, - HDCP_STATE_NAME, (link0_status & BIT(8)) >> 8, - (link0_status & BIT(9)) >> 9); + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + (link0_status & (BIT(8) | BIT(9))), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: An not ready\n", __func__); goto error; } /* As per hardware recommendations, wait before reading An */ msleep(20); - /* Read An0 and An1 */ - link0_an_0 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA5); - link0_an_1 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA6); + /* + * As per hardware recommendation, for DP, read AN0 and AN1 again + * with a delay of 1 micro second each. + */ + link0_an_0 = DSS_REG_R(io, reg_set->data5); + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) { + udelay(1); + link0_an_0 = DSS_REG_R(io, reg_set->data5); + } + + link0_an_1 = DSS_REG_R(io, reg_set->data6); + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) { + udelay(1); + link0_an_0 = DSS_REG_R(io, reg_set->data6); + } /* Read AKSV */ - link0_aksv_0 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA3); - link0_aksv_1 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA4); + link0_aksv_0 = DSS_REG_R(io, reg_set->data3); + link0_aksv_1 = DSS_REG_R(io, reg_set->data4); /* Copy An and AKSV to byte arrays for transmission */ aksv[0] = link0_aksv_0 & 0xFF; @@ -510,55 +754,21 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) an[6] = (link0_an_1 >> 16) & 0xFF; an[7] = (link0_an_1 >> 24) & 0xFF; - /* Write An to offset 0x18 */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x18; - ddc_data.data_buf = an; - ddc_data.data_len = 8; - ddc_data.what = "An"; - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: An write failed\n", __func__, HDCP_STATE_NAME); + rc = hdmi_hdcp_write(hdcp_ctrl, 0x18, 8, an, "an"); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error writing an to sink\n", __func__); goto error; } - /* Write AKSV to offset 0x10 */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x10; - ddc_data.data_buf = aksv; - ddc_data.data_len = 5; - ddc_data.what = "Aksv"; - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: AKSV write failed\n", __func__, - HDCP_STATE_NAME); + rc = hdmi_hdcp_write(hdcp_ctrl, 0x10, 5, aksv, "aksv"); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error writing aksv to sink\n", __func__); goto error; } - DEV_DBG("%s: %s: Link0-AKSV=%02x%08x\n", __func__, - HDCP_STATE_NAME, link0_aksv_1 & 0xFF, link0_aksv_0); - - /* Read BKSV at offset 0x00 */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x00; - ddc_data.data_buf = bksv; - ddc_data.data_len = 5; - ddc_data.request_len = 5; - ddc_data.retry = 5; - ddc_data.what = "Bksv"; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: BKSV read failed\n", __func__, - HDCP_STATE_NAME); + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.bksv, bksv, false); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading bksv from sink\n", __func__); goto error; } @@ -584,9 +794,9 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) if (hdcp_ctrl->tz_hdcp) { memset(scm_buf, 0x00, sizeof(scm_buf)); - scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA0; + scm_buf[0].addr = phy_addr + reg_set->data0; scm_buf[0].val = link0_bksv_0; - scm_buf[1].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA1; + scm_buf[1].addr = phy_addr + reg_set->data1; scm_buf[1].val = link0_bksv_1; ret = hdcp_scm_call(scm_buf, &resp); @@ -597,52 +807,45 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { - DSS_REG_W(hdcp_io, HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, - link0_bksv_0); - DSS_REG_W(hdcp_io, HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, - link0_bksv_1); + } else if (hdcp_ctrl->init_data.sec_access) { + DSS_REG_W(hdcp_io, reg_set->sec_data0, link0_bksv_0); + DSS_REG_W(hdcp_io, reg_set->sec_data1, link0_bksv_1); } else { - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA0, link0_bksv_0); - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA1, link0_bksv_1); + DSS_REG_W(io, reg_set->data0, link0_bksv_0); + DSS_REG_W(io, reg_set->data1, link0_bksv_1); } - /* Enable HDCP interrupts and ack/clear any stale interrupts */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0xE6); - /* * HDCP Compliace Test case 1A-01: * Wait here at least 100ms before reading R0' */ msleep(125); - /* Read R0' at offset 0x08 */ + /* Wait for HDCP R0 computation to be completed */ + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + link0_status & BIT(reg_set->r0_offset), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: R0 not ready\n", __func__); + goto error; + } + memset(buf, 0, sizeof(buf)); - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x08; - ddc_data.data_buf = buf; - ddc_data.data_len = 2; - ddc_data.request_len = 2; - ddc_data.retry = 5; - ddc_data.what = "R0'"; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: R0' read failed\n", __func__, HDCP_STATE_NAME); + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.r0, buf, false); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading R0' from sink\n", __func__); goto error; } + DEV_DBG("%s: %s: R0'=%02x%02x\n", __func__, HDCP_STATE_NAME, buf[1], buf[0]); /* Write R0' to HDCP registers and check to see if it is a match */ reinit_completion(&hdcp_ctrl->r0_checked); - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA2_0, (((u32)buf[1]) << 8) | buf[0]); + DSS_REG_W(io, reg_set->data2_0, (((u32)buf[1]) << 8) | buf[0]); timeout_count = wait_for_completion_timeout( &hdcp_ctrl->r0_checked, HZ*2); - link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); + link0_status = DSS_REG_R(io, reg_set->status); is_match = link0_status & BIT(12); if (!is_match) { DEV_DBG("%s: %s: Link0_Status=0x%08x\n", __func__, @@ -663,166 +866,108 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl) } error: - if (rc) { + if (rc) DEV_ERR("%s: %s: Authentication Part I failed\n", __func__, hdcp_ctrl ? HDCP_STATE_NAME : "???"); - } else { - /* Enable HDCP Encryption */ - DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0) | BIT(8)); + else DEV_INFO("%s: %s: Authentication Part I successful\n", __func__, HDCP_STATE_NAME); - } return rc; } /* hdmi_hdcp_authentication_part1 */ -#define READ_WRITE_V_H(io, off, name, reg, wr) \ -do { \ - ddc_data.offset = (off); \ - memset(what, 0, sizeof(what)); \ - snprintf(what, sizeof(what), (name)); \ - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; \ - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); \ - if (rc) { \ - DEV_ERR("%s: %s: Read %s failed\n", __func__, HDCP_STATE_NAME, \ - what); \ - goto error; \ - } \ - DEV_DBG("%s: %s: %s: buf[0]=%x, buf[1]=%x, buf[2]=%x, buf[3]=%x\n", \ - __func__, HDCP_STATE_NAME, what, buf[0], buf[1], \ - buf[2], buf[3]); \ - if (wr) { \ - DSS_REG_W((io), (reg), \ - (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); \ - } \ -} while (0); +static int hdmi_hdcp_set_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl, + struct hdmi_hdcp_reg_data *rd, u8 *buf) +{ + int rc; + struct dss_io_data *io; + + if (!hdcp_ctrl->tz_hdcp && hdcp_ctrl->init_data.sec_access) + io = hdcp_ctrl->init_data.hdcp_io; + else + io = hdcp_ctrl->init_data.core_io; + + rc = hdmi_hdcp_read(hdcp_ctrl, rd->sink, buf, false); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading %s\n", __func__, rd->sink->name); + goto end; + } + + DEV_DBG("%s: %s: %s: buf[0]=%x, buf[1]=%x, buf[2]=%x, buf[3]=%x\n", + __func__, HDCP_STATE_NAME, rd->sink->name, buf[0], buf[1], + buf[2], buf[3]); + + if (!hdcp_ctrl->tz_hdcp) + DSS_REG_W(io, rd->reg_id, + (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); +end: + return rc; +} static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) { - char what[20]; int rc = 0; u8 buf[4]; - struct hdmi_tx_ddc_data ddc_data; - struct dss_io_data *io; - struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG]; u32 phy_addr; - + struct hdcp_reg_set *reg_set = &hdcp_ctrl->reg_set; struct hdmi_hdcp_reg_data reg_data[] = { - {HDMI_HDCP_RCVPORT_DATA7, 0x20, "V' H0"}, - {HDMI_HDCP_RCVPORT_DATA8, 0x24, "V' H1"}, - {HDMI_HDCP_RCVPORT_DATA9, 0x28, "V' H2"}, - {HDMI_HDCP_RCVPORT_DATA10, 0x2C, "V' H3"}, - {HDMI_HDCP_RCVPORT_DATA11, 0x30, "V' H4"}, + {reg_set_data(7), &hdcp_ctrl->sink_addr.v_h0}, + {reg_set_data(8), &hdcp_ctrl->sink_addr.v_h1}, + {reg_set_data(9), &hdcp_ctrl->sink_addr.v_h2}, + {reg_set_data(10), &hdcp_ctrl->sink_addr.v_h3}, + {reg_set_data(11), &hdcp_ctrl->sink_addr.v_h4}, }; - u32 size = sizeof(reg_data)/sizeof(reg_data[0]); - u32 iter = 0; - u32 ret = 0; - u32 resp = 0; - - if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { - DEV_ERR("%s: invalid input\n", __func__); - return -EINVAL; - } + u32 size = ARRAY_SIZE(reg_data); + u32 iter = 0, ret = 0, resp = 0; phy_addr = hdcp_ctrl->init_data.phy_addr; - io = hdcp_ctrl->init_data.core_io; - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.data_buf = buf; - ddc_data.data_len = 4; - ddc_data.request_len = 4; - ddc_data.retry = 5; - ddc_data.what = what; - - if (hdcp_ctrl->tz_hdcp) { - memset(scm_buf, 0x00, sizeof(scm_buf)); + memset(scm_buf, 0x00, sizeof(scm_buf)); - for (iter = 0; iter < size && iter < SCM_HDCP_MAX_REG; iter++) { - struct hdmi_hdcp_reg_data *rd = reg_data + iter; + for (iter = 0; iter < size; iter++) { + struct hdmi_hdcp_reg_data *rd = reg_data + iter; - READ_WRITE_V_H(io, rd->off, rd->name, 0, false); + memset(buf, 0, sizeof(buf)); + hdmi_hdcp_set_v_h(hdcp_ctrl, rd, buf); - rd->reg_val = buf[3] << 24 | buf[2] << 16 | + if (hdcp_ctrl->tz_hdcp) { + u32 reg_val = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; scm_buf[iter].addr = phy_addr + reg_data[iter].reg_id; - scm_buf[iter].val = reg_data[iter].reg_val; - } + scm_buf[iter].val = reg_val; - ret = hdcp_scm_call(scm_buf, &resp); - if (ret || resp) { - DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n", - __func__, ret, resp); - rc = -EINVAL; - goto error; + ret = hdcp_scm_call(scm_buf, &resp); + if (ret || resp) { + DEV_ERR("%s: scm err: ret=%d, resp=%d\n", + __func__, ret, resp); + rc = -EINVAL; + goto error; + } } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { - struct dss_io_data *hdcp_io = hdcp_ctrl->init_data.hdcp_io; - - /* Read V'.HO 4 Byte at offset 0x20 */ - READ_WRITE_V_H(hdcp_io, 0x20, "V' H0", - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, true); - - /* Read V'.H1 4 Byte at offset 0x24 */ - READ_WRITE_V_H(hdcp_io, 0x24, "V' H1", - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, true); - - /* Read V'.H2 4 Byte at offset 0x28 */ - READ_WRITE_V_H(hdcp_io, 0x28, "V' H2", - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, true); - - /* Read V'.H3 4 Byte at offset 0x2C */ - READ_WRITE_V_H(hdcp_io, 0x2C, "V' H3", - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, true); - - /* Read V'.H4 4 Byte at offset 0x30 */ - READ_WRITE_V_H(hdcp_io, 0x30, "V' H4", - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, true); - } else { - /* Read V'.HO 4 Byte at offset 0x20 */ - READ_WRITE_V_H(io, 0x20, "V' H0", HDMI_HDCP_RCVPORT_DATA7, - true); - - /* Read V'.H1 4 Byte at offset 0x24 */ - READ_WRITE_V_H(io, 0x24, "V' H1", HDMI_HDCP_RCVPORT_DATA8, - true); - - /* Read V'.H2 4 Byte at offset 0x28 */ - READ_WRITE_V_H(io, 0x28, "V' H2", HDMI_HDCP_RCVPORT_DATA9, - true); - - /* Read V'.H3 4 Byte at offset 0x2C */ - READ_WRITE_V_H(io, 0x2C, "V' H3", HDMI_HDCP_RCVPORT_DATA10, - true); - - /* Read V'.H4 4 Byte at offset 0x30 */ - READ_WRITE_V_H(io, 0x30, "V' H4", HDMI_HDCP_RCVPORT_DATA11, - true); } - error: return rc; } static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) { - int rc, cnt, i; - struct hdmi_tx_ddc_data ddc_data; + int rc, i; u32 timeout_count, down_stream_devices = 0; u32 repeater_cascade_depth = 0; u8 buf[0xFF]; u8 *ksv_fifo = NULL; - u8 bcaps; - u16 bstatus, max_devs_exceeded = 0, max_cascade_exceeded = 0; - u32 link0_status; + u8 bcaps = 0; + u16 bstatus = 0, max_devs_exceeded = 0, max_cascade_exceeded = 0; + u32 status = 0, sha_status = 0; u32 ksv_bytes; struct dss_io_data *io; - + struct hdcp_reg_set *reg_set; struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG]; u32 phy_addr; u32 ret = 0; u32 resp = 0; + u32 ksv_read_retry = 20; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { DEV_ERR("%s: invalid input\n", __func__); @@ -831,6 +976,7 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) } phy_addr = hdcp_ctrl->init_data.phy_addr; + reg_set = &hdcp_ctrl->reg_set; if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) { DEV_DBG("%s: %s: invalid state. returning\n", __func__, @@ -844,8 +990,7 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) io = hdcp_ctrl->init_data.core_io; memset(buf, 0, sizeof(buf)); - memset(ksv_fifo, 0, - sizeof(hdcp_ctrl->current_tp.ksv_list)); + memset(ksv_fifo, 0, sizeof(hdcp_ctrl->current_tp.ksv_list)); /* * Wait until READY bit is set in BCAPS, as per HDCP specifications @@ -853,48 +998,22 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) */ timeout_count = 50; do { - timeout_count--; - /* Read BCAPS at offset 0x40 */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x40; - ddc_data.data_buf = &bcaps; - ddc_data.data_len = 1; - ddc_data.request_len = 1; - ddc_data.retry = 5; - ddc_data.what = "Bcaps"; - ddc_data.retry_align = true; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: BCAPS read failed\n", __func__, - HDCP_STATE_NAME); + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.bcaps, + &bcaps, true); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading bcaps\n", __func__); goto error; } msleep(100); - } while (!(bcaps & BIT(5)) && timeout_count); - - /* Read BSTATUS at offset 0x41 */ - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x41; - ddc_data.data_buf = buf; - ddc_data.data_len = 2; - ddc_data.request_len = 2; - ddc_data.retry = 5; - ddc_data.what = "Bstatuss"; - ddc_data.retry_align = true; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: BSTATUS read failed\n", __func__, - HDCP_STATE_NAME); + } while (!(bcaps & BIT(5)) && --timeout_count); + + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.bstatus, + buf, true); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: error reading bstatus\n", __func__); goto error; } + bstatus = buf[1]; bstatus = (bstatus << 8) | buf[0]; @@ -902,7 +1021,7 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) memset(scm_buf, 0x00, sizeof(scm_buf)); /* Write BSTATUS and BCAPS to HDCP registers */ - scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA12; + scm_buf[0].addr = phy_addr + reg_set->data12; scm_buf[0].val = bcaps | (bstatus << 8); ret = hdcp_scm_call(scm_buf, &resp); @@ -912,12 +1031,12 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { + } else if (hdcp_ctrl->init_data.sec_access) { DSS_REG_W(hdcp_ctrl->init_data.hdcp_io, - HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, - bcaps | (bstatus << 8)); + reg_set->sec_data12, + bcaps | (bstatus << 8)); } else { - DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps | (bstatus << 8)); + DSS_REG_W(io, reg_set->data12, bcaps | (bstatus << 8)); } down_stream_devices = bstatus & 0x7F; @@ -972,37 +1091,28 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) * HDCP Repeaters (REPEATER == 0). */ ksv_bytes = 5 * down_stream_devices; - memset(&ddc_data, 0, sizeof(ddc_data)); - ddc_data.dev_addr = 0x74; - ddc_data.offset = 0x43; - ddc_data.data_buf = ksv_fifo; - ddc_data.data_len = ksv_bytes; - ddc_data.request_len = ksv_bytes; - ddc_data.retry = 5; - ddc_data.what = "KSV FIFO"; - - hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data; - - cnt = 0; + hdcp_ctrl->sink_addr.ksv_fifo.len = ksv_bytes; + do { - rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl); - if (rc) { - DEV_ERR("%s: %s: KSV FIFO read failed\n", __func__, - HDCP_STATE_NAME); + rc = hdmi_hdcp_read(hdcp_ctrl, &hdcp_ctrl->sink_addr.ksv_fifo, + ksv_fifo, false); + if (IS_ERR_VALUE(rc)) { + DEV_DBG("%s: could not read ksv fifo (%d)\n", + __func__, ksv_read_retry); /* * HDCP Compliace Test case 1B-01: * Wait here until all the ksv bytes have been * read from the KSV FIFO register. */ msleep(25); - } else { - break; + } - cnt++; - } while (cnt != 20); + } while (rc && --ksv_read_retry); - if (cnt == 20) + if (rc) { + DEV_ERR("%s: error reading ksv_fifo\n", __func__); goto error; + } rc = hdmi_hdcp_transfer_v_h(hdcp_ctrl); if (rc) @@ -1019,9 +1129,9 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) if (hdcp_ctrl->tz_hdcp) { memset(scm_buf, 0x00, sizeof(scm_buf)); - scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_CTRL; + scm_buf[0].addr = phy_addr + reg_set->sha_ctrl; scm_buf[0].val = HDCP_REG_ENABLE; - scm_buf[1].addr = phy_addr + HDMI_HDCP_SHA_CTRL; + scm_buf[1].addr = phy_addr + reg_set->sha_ctrl; scm_buf[1].val = HDCP_REG_DISABLE; ret = hdcp_scm_call(scm_buf, &resp); @@ -1031,16 +1141,16 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { + } else if (hdcp_ctrl->init_data.sec_access) { DSS_REG_W(hdcp_ctrl->init_data.hdcp_io, - HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, + reg_set->sec_sha_ctrl, HDCP_REG_ENABLE); DSS_REG_W(hdcp_ctrl->init_data.hdcp_io, - HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, + reg_set->sec_sha_ctrl, HDCP_REG_DISABLE); } else { - DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, HDCP_REG_ENABLE); - DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, HDCP_REG_DISABLE); + DSS_REG_W(io, reg_set->sha_ctrl, HDCP_REG_ENABLE); + DSS_REG_W(io, reg_set->sha_ctrl, HDCP_REG_DISABLE); } for (i = 0; i < ksv_bytes - 1; i++) { @@ -1048,7 +1158,7 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) if (hdcp_ctrl->tz_hdcp) { memset(scm_buf, 0x00, sizeof(scm_buf)); - scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_DATA; + scm_buf[0].addr = phy_addr + reg_set->sha_ctrl; scm_buf[0].val = ksv_fifo[i] << 16; ret = hdcp_scm_call(scm_buf, &resp); @@ -1058,12 +1168,12 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { + } else if (hdcp_ctrl->init_data.sec_access) { DSS_REG_W_ND(hdcp_ctrl->init_data.hdcp_io, - HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA, + reg_set->sec_sha_ctrl, ksv_fifo[i] << 16); } else { - DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA, ksv_fifo[i] << 16); + DSS_REG_W_ND(io, reg_set->sha_ctrl, ksv_fifo[i] << 16); } /* @@ -1071,31 +1181,22 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) * HDCP_SHA_BLOCK_DONE before writing any further */ if (i && !((i + 1) % 64)) { - timeout_count = 100; - while (!(DSS_REG_R(io, HDMI_HDCP_SHA_STATUS) & BIT(0)) - && (--timeout_count)) { - DEV_DBG("%s: %s: Wrote 64 bytes KSV FIFO\n", - __func__, HDCP_STATE_NAME); - DEV_DBG("%s: %s: HDCP_SHA_STATUS=%08x\n", - __func__, HDCP_STATE_NAME, - DSS_REG_R(io, HDMI_HDCP_SHA_STATUS)); - msleep(20); - } - if (!timeout_count) { - rc = -ETIMEDOUT; - DEV_ERR("%s: %s: Write KSV FIFO timedout", - __func__, HDCP_STATE_NAME); + rc = readl_poll_timeout(io->base + reg_set->sha_status, + sha_status, sha_status & BIT(0), + HDCP_POLL_SLEEP_US, + HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: block not done\n", __func__); goto error; } } - } /* Write l to DONE bit[0] */ if (hdcp_ctrl->tz_hdcp) { memset(scm_buf, 0x00, sizeof(scm_buf)); - scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_DATA; + scm_buf[0].addr = phy_addr + reg_set->sha_ctrl; scm_buf[0].val = (ksv_fifo[ksv_bytes - 1] << 16) | 0x1; ret = hdcp_scm_call(scm_buf, &resp); @@ -1105,43 +1206,32 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl) rc = -EINVAL; goto error; } - } else if (hdcp_ctrl->hdmi_tx_ver_4) { + } else if (hdcp_ctrl->init_data.sec_access) { DSS_REG_W_ND(hdcp_ctrl->init_data.hdcp_io, - HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA, + reg_set->sec_sha_ctrl, (ksv_fifo[ksv_bytes - 1] << 16) | 0x1); } else { - DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA, + DSS_REG_W_ND(io, reg_set->sha_ctrl, (ksv_fifo[ksv_bytes - 1] << 16) | 0x1); } /* Now wait for HDCP_SHA_COMP_DONE */ - timeout_count = 100; - while ((0x10 != (DSS_REG_R(io, HDMI_HDCP_SHA_STATUS) - & 0xFFFFFF10)) && --timeout_count) - msleep(20); - if (!timeout_count) { - rc = -ETIMEDOUT; - DEV_ERR("%s: %s: SHA computation timedout", __func__, - HDCP_STATE_NAME); + rc = readl_poll_timeout(io->base + reg_set->sha_status, sha_status, + sha_status & BIT(4), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: comp not done\n", __func__); goto error; } /* Wait for V_MATCHES */ - timeout_count = 100; - link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); - while (((link0_status & BIT(20)) != BIT(20)) && --timeout_count) { - DEV_DBG("%s: %s: Waiting for V_MATCHES(%d). l0_status=0x%08x\n", - __func__, HDCP_STATE_NAME, timeout_count, link0_status); - msleep(20); - link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); - } - if (!timeout_count) { - rc = -ETIMEDOUT; - DEV_ERR("%s: %s: HDCP V Match timedout", __func__, - HDCP_STATE_NAME); + rc = readl_poll_timeout(io->base + reg_set->status, status, + status & BIT(reg_set->v_offset), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + DEV_ERR("%s: V not ready\n", __func__); goto error; } - error: if (rc) DEV_ERR("%s: %s: Authentication Part II failed\n", __func__, @@ -1236,7 +1326,8 @@ static void hdmi_hdcp_auth_work(struct work_struct *work) io = hdcp_ctrl->init_data.core_io; /* Enabling Software DDC */ - DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION , DSS_REG_R(io, + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) + DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, HDMI_DDC_ARBITRATION) & ~(BIT(4))); rc = hdmi_hdcp_authentication_part1(hdcp_ctrl); @@ -1258,7 +1349,8 @@ static void hdmi_hdcp_auth_work(struct work_struct *work) } /* Disabling software DDC before going into part3 to make sure * there is no Arbitration between software and hardware for DDC */ - DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION , DSS_REG_R(io, + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) + DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, HDMI_DDC_ARBITRATION) | (BIT(4))); error: @@ -1314,12 +1406,17 @@ int hdmi_hdcp_authenticate(void *input) DEV_DBG("%s: %s: Queuing work to start HDCP authentication", __func__, HDCP_STATE_NAME); - if (!hdmi_hdcp_load_keys(input)) + if (!hdmi_hdcp_load_keys(input)) { + flush_delayed_work(&hdcp_ctrl->hdcp_auth_work); + queue_delayed_work(hdcp_ctrl->init_data.workq, &hdcp_ctrl->hdcp_auth_work, HZ/2); - else + } else { + flush_work(&hdcp_ctrl->hdcp_int_work); + queue_work(hdcp_ctrl->init_data.workq, &hdcp_ctrl->hdcp_int_work); + } return 0; } /* hdmi_hdcp_authenticate */ @@ -1328,6 +1425,8 @@ int hdmi_hdcp_reauthenticate(void *input) { struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input; struct dss_io_data *io; + struct hdcp_reg_set *reg_set; + struct hdcp_int_set *isr; u32 hdmi_hw_version; u32 ret = 0; @@ -1337,6 +1436,8 @@ int hdmi_hdcp_reauthenticate(void *input) } io = hdcp_ctrl->init_data.core_io; + reg_set = &hdcp_ctrl->reg_set; + isr = &hdcp_ctrl->int_set; if (HDCP_STATE_AUTH_FAIL != hdcp_ctrl->hdcp_state) { DEV_DBG("%s: %s: invalid state. returning\n", __func__, @@ -1344,22 +1445,25 @@ int hdmi_hdcp_reauthenticate(void *input) return 0; } - hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION); - if (hdmi_hw_version >= 0x30030000) { - DSS_REG_W(io, HDMI_CTRL_SW_RESET, BIT(1)); - DSS_REG_W(io, HDMI_CTRL_SW_RESET, 0); + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) { + hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION); + if (hdmi_hw_version >= 0x30030000) { + DSS_REG_W(io, HDMI_CTRL_SW_RESET, BIT(1)); + DSS_REG_W(io, HDMI_CTRL_SW_RESET, 0); + } + + /* Wait to be clean on DDC HW engine */ + hdmi_hdcp_hw_ddc_clean(hdcp_ctrl); } /* Disable HDCP interrupts */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0); - - DSS_REG_W(io, HDMI_HDCP_RESET, BIT(0)); + DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); - /* Wait to be clean on DDC HW engine */ - hdmi_hdcp_hw_ddc_clean(hdcp_ctrl); + if (reg_set->reset) + DSS_REG_W(io, reg_set->reset, BIT(0)); /* Disable encryption and disable the HDCP block */ - DSS_REG_W(io, HDMI_HDCP_CTRL, 0); + DSS_REG_W(io, reg_set->ctrl, 0); if (!hdmi_hdcp_load_keys(input)) queue_delayed_work(hdcp_ctrl->init_data.workq, @@ -1375,6 +1479,8 @@ void hdmi_hdcp_off(void *input) { struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input; struct dss_io_data *io; + struct hdcp_reg_set *reg_set; + struct hdcp_int_set *isr; int rc = 0; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { @@ -1383,6 +1489,8 @@ void hdmi_hdcp_off(void *input) } io = hdcp_ctrl->init_data.core_io; + reg_set = &hdcp_ctrl->reg_set; + isr = &hdcp_ctrl->int_set; if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) { DEV_DBG("%s: %s: inactive. returning\n", __func__, @@ -1396,7 +1504,8 @@ void hdmi_hdcp_off(void *input) * reauth works will know that the HDCP session has been turned off. */ mutex_lock(hdcp_ctrl->init_data.mutex); - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0); + DSS_REG_W(io, isr->int_reg, + DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; mutex_unlock(hdcp_ctrl->init_data.mutex); @@ -1415,10 +1524,11 @@ void hdmi_hdcp_off(void *input) DEV_DBG("%s: %s: Deleted hdcp int work\n", __func__, HDCP_STATE_NAME); - DSS_REG_W(io, HDMI_HDCP_RESET, BIT(0)); + if (reg_set->reset) + DSS_REG_W(io, reg_set->reset, BIT(0)); /* Disable encryption and disable the HDCP block */ - DSS_REG_W(io, HDMI_HDCP_CTRL, 0); + DSS_REG_W(io, reg_set->ctrl, 0); DEV_DBG("%s: %s: HDCP: Off\n", __func__, HDCP_STATE_NAME); } /* hdmi_hdcp_off */ @@ -1429,6 +1539,8 @@ int hdmi_hdcp_isr(void *input) int rc = 0; struct dss_io_data *io; u32 hdcp_int_val; + struct hdcp_reg_set *reg_set; + struct hdcp_int_set *isr; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { DEV_ERR("%s: invalid input\n", __func__); @@ -1437,28 +1549,33 @@ int hdmi_hdcp_isr(void *input) } io = hdcp_ctrl->init_data.core_io; + reg_set = &hdcp_ctrl->reg_set; + isr = &hdcp_ctrl->int_set; - hdcp_int_val = DSS_REG_R(io, HDMI_HDCP_INT_CTRL); + hdcp_int_val = DSS_REG_R(io, isr->int_reg); /* Ignore HDCP interrupts if HDCP is disabled */ if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) { - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, HDCP_INT_CLR); + DSS_REG_W(io, isr->int_reg, hdcp_int_val | HDCP_INT_CLR); return 0; } - if (hdcp_int_val & BIT(0)) { + if (hdcp_int_val & isr->auth_success_int) { /* AUTH_SUCCESS_INT */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(1))); + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_success_ack)); DEV_INFO("%s: %s: AUTH_SUCCESS_INT received\n", __func__, HDCP_STATE_NAME); if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) complete_all(&hdcp_ctrl->r0_checked); } - if (hdcp_int_val & BIT(4)) { + if (hdcp_int_val & isr->auth_fail_int) { /* AUTH_FAIL_INT */ - u32 link_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS); - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(5))); + u32 link_status = DSS_REG_R(io, reg_set->status); + + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_fail_ack)); DEV_INFO("%s: %s: AUTH_FAIL_INT rcvd, LINK0_STATUS=0x%08x\n", __func__, HDCP_STATE_NAME, link_status); if (HDCP_STATE_AUTHENTICATED == hdcp_ctrl->hdcp_state) { @@ -1471,23 +1588,42 @@ int hdmi_hdcp_isr(void *input) } /* Clear AUTH_FAIL_INFO as well */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(7))); + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_fail_info_ack)); } - if (hdcp_int_val & BIT(8)) { + if (hdcp_int_val & isr->tx_req_int) { /* DDC_XFER_REQ_INT */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(9))); + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->tx_req_ack)); DEV_INFO("%s: %s: DDC_XFER_REQ_INT received\n", __func__, HDCP_STATE_NAME); } - if (hdcp_int_val & BIT(12)) { + if (hdcp_int_val & isr->tx_req_done_int) { /* DDC_XFER_DONE_INT */ - DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(13))); + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->tx_req_done_ack)); DEV_INFO("%s: %s: DDC_XFER_DONE received\n", __func__, HDCP_STATE_NAME); } + if (hdcp_int_val & isr->encryption_ready) { + /* Encryption enabled */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->encryption_ready_ack)); + DEV_INFO("%s: %s: encryption ready received\n", __func__, + HDCP_STATE_NAME); + } + + if (hdcp_int_val & isr->encryption_not_ready) { + /* Encryption enabled */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->encryption_not_ready_ack)); + DEV_INFO("%s: %s: encryption not ready received\n", __func__, + HDCP_STATE_NAME); + } + error: return rc; } /* hdmi_hdcp_isr */ @@ -1605,6 +1741,27 @@ void hdmi_hdcp_deinit(void *input) kfree(hdcp_ctrl); } /* hdmi_hdcp_deinit */ +static void hdmi_hdcp_update_client_reg_set(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{ + if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI) { + struct hdcp_reg_set reg_set = HDCP_REG_SET_CLIENT_HDMI; + struct hdcp_sink_addr_map sink_addr = HDCP_HDMI_SINK_ADDR_MAP; + struct hdcp_int_set isr = HDCP_HDMI_INT_SET; + + hdcp_ctrl->reg_set = reg_set; + hdcp_ctrl->sink_addr = sink_addr; + hdcp_ctrl->int_set = isr; + } else if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) { + struct hdcp_reg_set reg_set = HDCP_REG_SET_CLIENT_DP; + struct hdcp_sink_addr_map sink_addr = HDCP_DP_SINK_ADDR_MAP; + struct hdcp_int_set isr = HDCP_DP_INT_SET; + + hdcp_ctrl->reg_set = reg_set; + hdcp_ctrl->sink_addr = sink_addr; + hdcp_ctrl->int_set = isr; + } +} + void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data) { struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL; @@ -1624,9 +1781,8 @@ void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data) goto error; } - if (init_data->hdmi_tx_ver >= HDMI_TX_VERSION_4 - && !init_data->hdcp_io) { - DEV_ERR("%s: hdcp_io required for HDMI Tx Ver 4\n", __func__); + if (init_data->sec_access && !init_data->hdcp_io) { + DEV_ERR("%s: hdcp_io required\n", __func__); goto error; } @@ -1638,8 +1794,8 @@ void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data) hdcp_ctrl->init_data = *init_data; hdcp_ctrl->ops = &ops; - hdcp_ctrl->hdmi_tx_ver_4 = - (init_data->hdmi_tx_ver >= HDMI_TX_VERSION_4); + + hdmi_hdcp_update_client_reg_set(hdcp_ctrl); if (sysfs_create_group(init_data->sysfs_kobj, &hdmi_hdcp_fs_attr_group)) { @@ -1653,7 +1809,7 @@ void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data) hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; init_completion(&hdcp_ctrl->r0_checked); - if (!hdcp_ctrl->hdmi_tx_ver_4) { + if (!hdcp_ctrl->init_data.sec_access) { ret = scm_is_call_available(SCM_SVC_HDCP, SCM_CMD_HDCP); if (ret <= 0) { DEV_ERR("%s: secure hdcp service unavailable, ret = %d", diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h index 7dda3ff5c67d..4f2bdc4bfd3d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2014-2015 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2014-2016, 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 @@ -17,6 +17,11 @@ #include <video/msm_hdmi_modes.h> #include <soc/qcom/scm.h> +enum hdcp_client_id { + HDCP_CLIENT_HDMI, + HDCP_CLIENT_DP, +}; + enum hdmi_hdcp_state { HDCP_STATE_INACTIVE, HDCP_STATE_AUTHENTICATING, @@ -37,10 +42,13 @@ struct hdmi_hdcp_init_data { void *cb_data; void (*notify_status)(void *cb_data, enum hdmi_hdcp_state status); struct hdmi_tx_ddc_ctrl *ddc_ctrl; + void *dp_data; u32 phy_addr; u32 hdmi_tx_ver; struct msm_hdmi_mode_timing_info *timing; bool tethered; + bool sec_access; + enum hdcp_client_id client_id; }; struct hdmi_hdcp_ops { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index cd3017219005..050c4a4cecdf 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -1801,6 +1801,7 @@ static int hdmi_tx_init_hdcp(struct hdmi_tx_ctrl *hdmi_ctrl) hdcp_init_data.notify_status = hdmi_tx_hdcp_cb; hdcp_init_data.cb_data = (void *)hdmi_ctrl; hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_major_version; + hdcp_init_data.sec_access = true; hdcp_init_data.timing = &hdmi_ctrl->timing; if (hdmi_ctrl->hdcp14_present) { diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index ca91742400ab..e1aa004e14e6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1732,6 +1732,24 @@ static int mdss_mdp_irq_clk_setup(struct mdss_data_type *mdata) pr_debug("unable to get CX reg. rc=%d\n", PTR_RET(mdata->vdd_cx)); mdata->vdd_cx = NULL; + } else { + /* Parse CX voltage settings */ + ret = of_property_read_u32(mdata->pdev->dev.of_node, + "vdd-cx-min-uV", &mdata->vdd_cx_min_uv); + if (ret) { + pr_err("min uV for vdd-cx not specified. rc=%d\n", ret); + return ret; + } + + ret = of_property_read_u32(mdata->pdev->dev.of_node, + "vdd-cx-max-uV", &mdata->vdd_cx_max_uv); + if (ret) { + pr_err("max uV for vdd-cx not specified. rc=%d\n", ret); + return ret; + } + + pr_debug("vdd_cx [min_uV, max_uV] = [%d %d]\n", + mdata->vdd_cx_min_uv, mdata->vdd_cx_max_uv); } mdata->reg_bus_clt = mdss_reg_bus_vote_client_create("mdp\0"); @@ -1976,7 +1994,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_mdp_init_default_prefill_factors(mdata); mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU); mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT); - mdss_set_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS); mdata->has_wb_ubwc = true; set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map); set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map); @@ -4779,8 +4796,8 @@ static int mdss_mdp_cx_ctrl(struct mdss_data_type *mdata, int enable) if (enable) { rc = regulator_set_voltage( mdata->vdd_cx, - RPM_REGULATOR_CORNER_SVS_SOC, - RPM_REGULATOR_CORNER_SUPER_TURBO); + mdata->vdd_cx_min_uv, + mdata->vdd_cx_max_uv); if (rc < 0) goto vreg_set_voltage_fail; @@ -4799,8 +4816,8 @@ static int mdss_mdp_cx_ctrl(struct mdss_data_type *mdata, int enable) } rc = regulator_set_voltage( mdata->vdd_cx, - RPM_REGULATOR_CORNER_NONE, - RPM_REGULATOR_CORNER_SUPER_TURBO); + 0, + mdata->vdd_cx_max_uv); if (rc < 0) goto vreg_set_voltage_fail; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 3a39d4fdc895..600701041309 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -347,10 +347,7 @@ static void __update_avr_info(struct mdss_mdp_ctl *ctl, */ static bool __layer_needs_src_split(struct mdp_input_layer *layer) { - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - - return (layer->flags & MDP_LAYER_ASYNC) || - mdss_has_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS); + return (layer->flags & MDP_LAYER_ASYNC); } static int __async_update_position_check(struct msm_fb_data_type *mfd, diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index bcb4867b4ffd..8cfb8e46777c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -2690,6 +2690,16 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, if (pipe->scaler.enable) mdss_mdp_pipe_program_pixel_extn(pipe); + + ret = mdss_mdp_pipe_pp_setup(pipe, &opmode); + if (ret) { + pr_err("pipe pp setup error for pnum=%d\n", pipe->num); + + MDSS_XLOG(pipe->num, pipe->mixer_left->num, + pipe->play_cnt, 0xbad); + + goto done; + } } if ((!(pipe->flags & MDP_VPU_PIPE) && (src_data == NULL)) || @@ -2708,12 +2718,6 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, if (params_changed) { pipe->params_changed = 0; - ret = mdss_mdp_pipe_pp_setup(pipe, &opmode); - if (ret) { - pr_err("pipe pp setup error for pnum=%d\n", pipe->num); - goto done; - } - ret = mdss_mdp_image_setup(pipe, src_data); if (ret) { pr_err("image setup error for pnum=%d\n", pipe->num); diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c index 5474df66eefb..a21242870a35 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/video/fbdev/msm/msm_ext_display.c @@ -360,10 +360,13 @@ static int msm_ext_disp_hpd(struct platform_device *pdev, ext_disp->current_disp = data->type; } else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) && !ext_disp->ack_enabled) { - ext_disp->ops->audio_info_setup = NULL; - ext_disp->ops->get_audio_edid_blk = NULL; - ext_disp->ops->cable_status = NULL; - ext_disp->ops->get_intf_id = NULL; + if (ext_disp->ops) { + ext_disp->ops->audio_info_setup = NULL; + ext_disp->ops->get_audio_edid_blk = NULL; + ext_disp->ops->cable_status = NULL; + ext_disp->ops->get_intf_id = NULL; + } + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; } @@ -451,7 +454,7 @@ static int msm_ext_disp_notify(struct platform_device *pdev, if (ret) goto end; - if (new_state == EXT_DISPLAY_CABLE_CONNECT) { + if (new_state == EXT_DISPLAY_CABLE_CONNECT && ext_disp->ops) { ext_disp->ops->audio_info_setup = data->codec_ops.audio_info_setup; ext_disp->ops->get_audio_edid_blk = @@ -524,10 +527,13 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) * empty. */ if (!ack_hpd) { - ext_disp->ops->audio_info_setup = NULL; - ext_disp->ops->get_audio_edid_blk = NULL; - ext_disp->ops->cable_status = NULL; - ext_disp->ops->get_intf_id = NULL; + if (ext_disp->ops) { + ext_disp->ops->audio_info_setup = NULL; + ext_disp->ops->get_audio_edid_blk = NULL; + ext_disp->ops->cable_status = NULL; + ext_disp->ops->get_intf_id = NULL; + } + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; } |