From a962515cc6270639e7ec645409a0dd1b0c848829 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Thu, 30 Jan 2020 14:57:55 +0100 Subject: [PATCH 17/17] ARM v2018.11 stm32mp r4 MISC --- Makefile | 2 +- cmd/remoteproc.c | 4 - drivers/clk/clk_stm32mp1.c | 284 +++++++++- drivers/i2c/stm32f7_i2c.c | 116 ++-- drivers/mmc/mmc-uclass.c | 14 + drivers/mmc/mmc.c | 15 + drivers/mmc/stm32_sdmmc2.c | 13 +- drivers/net/dwc_eth_qos.c | 47 +- drivers/ram/stm32mp1/stm32mp1_ddr.c | 30 +- drivers/ram/stm32mp1/stm32mp1_interactive.c | 4 +- drivers/remoteproc/rproc-uclass.c | 35 +- drivers/remoteproc/stm32_copro.c | 9 +- drivers/video/Kconfig | 13 +- drivers/video/Makefile | 5 +- drivers/video/dsi-host-uclass.c | 39 ++ drivers/video/dw_mipi_dsi.c | 140 ++--- drivers/video/mipi_display.c | 817 --------------------------- drivers/video/mipi_dsi.c | 828 ++++++++++++++++++++++++++++ drivers/video/orisetech_otm8009a.c | 101 ++-- drivers/video/raydium-rm68200.c | 39 +- drivers/video/stm32/stm32_dsi.c | 167 ++++-- drivers/video/stm32/stm32_ltdc.c | 50 +- include/dm/uclass-id.h | 1 + include/dsi_host.h | 57 ++ include/mipi_display.h | 249 +-------- include/mipi_dsi.h | 473 ++++++++++++++++ include/mmc.h | 13 + include/power/stpmic1.h | 1 + include/remoteproc.h | 11 + 29 files changed, 2188 insertions(+), 1389 deletions(-) create mode 100644 drivers/video/dsi-host-uclass.c delete mode 100644 drivers/video/mipi_display.c create mode 100644 drivers/video/mipi_dsi.c create mode 100644 include/dsi_host.h create mode 100644 include/mipi_dsi.h diff --git a/Makefile b/Makefile index b9579b2..4c72fc1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION = 2018 PATCHLEVEL = 11 SUBLEVEL = -EXTRAVERSION = -stm32mp-r3 +EXTRAVERSION = -stm32mp-r4 NAME = # *DOCUMENTATION* diff --git a/cmd/remoteproc.c b/cmd/remoteproc.c index 755e933..5665032 100644 --- a/cmd/remoteproc.c +++ b/cmd/remoteproc.c @@ -177,10 +177,6 @@ static int do_remoteproc_load_rsc_table(cmd_tbl_t *cmdtp, int flag, int argc, } ret = rproc_load_rsc_table(id, addr, size, &rsc_addr, &rsc_size); - if (!ret) { - env_set_hex("copro_rsc_addr", rsc_addr); - env_set_hex("copro_rsc_size", rsc_size); - } printf("Remote Processor %d resource table %s : 0x%08lx-0x%x\n", id, ret ? "Not found" : "Found", ret ? 0 : rsc_addr, diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 2d7b185..a6503e4 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -650,8 +650,18 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { }; #ifdef STM32MP1_CLOCK_TREE_INIT + /* define characteristic of PLL according type */ +#define DIVM_MIN 0 +#define DIVM_MAX 63 #define DIVN_MIN 24 +#define DIVP_MIN 0 +#define DIVP_MAX 127 +#define FRAC_MAX 8192 + +#define PLL1600_VCO_MIN 800000000 +#define PLL1600_VCO_MAX 1600000000 + static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { [PLL_800] = { .refclk_min = 4, @@ -1206,6 +1216,211 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk) } #ifdef STM32MP1_CLOCK_TREE_INIT + +bool stm32mp1_supports_opp(u32 opp_id, u32 cpu_type) +{ + unsigned int id; + + switch (opp_id) { + case 1: + case 2: + id = opp_id; + break; + default: + id = 1; /* default value */ + break; + } + + switch (cpu_type) { + case CPU_STM32MP157Fxx: + case CPU_STM32MP157Dxx: + case CPU_STM32MP153Fxx: + case CPU_STM32MP153Dxx: + case CPU_STM32MP151Fxx: + case CPU_STM32MP151Dxx: + return true; + default: + return id == 1; + } +} + +__weak void board_vddcore_init(u32 voltage_mv) +{ +} + +/* + * gets OPP parameters (frequency in KHz and voltage in mV) from + * an OPP table subnode. Platform HW support capabilities are also checked. + * Returns 0 on success and a negative FDT error code on failure. + */ +static int stm32mp1_get_opp(u32 cpu_type, ofnode subnode, + u32 *freq_khz, u32 *voltage_mv) +{ + u32 opp_hw; + u64 read_freq_64; + u32 read_voltage_32; + + *freq_khz = 0; + *voltage_mv = 0; + + opp_hw = ofnode_read_u32_default(subnode, "opp-supported-hw", 0); + if (opp_hw) + if (!stm32mp1_supports_opp(opp_hw, cpu_type)) + return -FDT_ERR_BADVALUE; + + read_freq_64 = ofnode_read_u64_default(subnode, "opp-hz", 0) / + 1000ULL; + read_voltage_32 = ofnode_read_u32_default(subnode, "opp-microvolt", 0) / + 1000U; + + if (!read_voltage_32 || !read_freq_64) + return -FDT_ERR_NOTFOUND; + + /* Frequency value expressed in KHz must fit on 32 bits */ + if (read_freq_64 > U32_MAX) + return -FDT_ERR_BADVALUE; + + /* Millivolt value must fit on 16 bits */ + if (read_voltage_32 > U16_MAX) + return -FDT_ERR_BADVALUE; + + *freq_khz = (u32)read_freq_64; + *voltage_mv = read_voltage_32; + + return 0; +} + +/* + * parses OPP table in DT and finds the parameters for the + * highest frequency supported by the HW platform. + * Returns 0 on success and a negative FDT error code on failure. + */ +int stm32mp1_get_max_opp_freq(struct stm32mp1_clk_priv *priv, u64 *freq_hz) +{ + ofnode node, subnode; + int ret; + u32 freq = 0U, voltage = 0U; + u32 cpu_type = get_cpu_type(); + + node = ofnode_by_compatible(ofnode_null(), "operating-points-v2"); + if (!ofnode_valid(node)) + return -FDT_ERR_NOTFOUND; + + ofnode_for_each_subnode(subnode, node) { + unsigned int read_freq; + unsigned int read_voltage; + + ret = stm32mp1_get_opp(cpu_type, subnode, + &read_freq, &read_voltage); + if (ret) + continue; + + if (read_freq > freq) { + freq = read_freq; + voltage = read_voltage; + } + } + + if (!freq || !voltage) + return -FDT_ERR_NOTFOUND; + + *freq_hz = (u64)1000U * freq; + board_vddcore_init(voltage); + + return 0; +} + +static int stm32mp1_pll1_opp(struct stm32mp1_clk_priv *priv, int clksrc, + u32 *pllcfg, u32 *fracv) +{ + u32 post_divm; + u32 input_freq; + u64 output_freq; + u64 freq; + u64 vco; + u32 divm, divn, divp, frac; + int i, ret; + u32 diff; + u32 best_diff = U32_MAX; + + /* PLL1 is 1600 */ + const u32 DIVN_MAX = stm32mp1_pll[PLL_1600].divn_max; + const u32 POST_DIVM_MIN = stm32mp1_pll[PLL_1600].refclk_min * 1000000U; + const u32 POST_DIVM_MAX = stm32mp1_pll[PLL_1600].refclk_max * 1000000U; + + ret = stm32mp1_get_max_opp_freq(priv, &output_freq); + if (ret) + return ret; + + switch (clksrc) { + case CLK_PLL12_HSI: + input_freq = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case CLK_PLL12_HSE: + input_freq = stm32mp1_clk_get_fixed(priv, _HSE); + break; + default: + return -EINTR; + } + + /* Following parameters have always the same value */ + pllcfg[PLLCFG_Q] = 0; + pllcfg[PLLCFG_R] = 0; + pllcfg[PLLCFG_O] = PQR(1, 0, 0); + + for (divm = DIVM_MAX; divm >= DIVM_MIN; divm--) { + post_divm = (u32)(input_freq / (divm + 1)); + if (post_divm < POST_DIVM_MIN || post_divm > POST_DIVM_MAX) + continue; + + for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { + freq = output_freq * (divm + 1) * (divp + 1); + divn = (u32)((freq / input_freq) - 1); + if (divn < DIVN_MIN || divn > DIVN_MAX) + continue; + + frac = (u32)(((freq * FRAC_MAX) / input_freq) - + ((divn + 1) * FRAC_MAX)); + /* 2 loops to refine the fractional part */ + for (i = 2; i != 0; i--) { + if (frac > FRAC_MAX) + break; + + vco = (post_divm * (divn + 1)) + + ((post_divm * (u64)frac) / + FRAC_MAX); + if (vco < (PLL1600_VCO_MIN / 2) || + vco > (PLL1600_VCO_MAX / 2)) { + frac++; + continue; + } + freq = vco / (divp + 1); + if (output_freq < freq) + diff = (u32)(freq - output_freq); + else + diff = (u32)(output_freq - freq); + if (diff < best_diff) { + pllcfg[PLLCFG_M] = divm; + pllcfg[PLLCFG_N] = divn; + pllcfg[PLLCFG_P] = divp; + *fracv = frac; + + if (diff == 0) + return 0; + + best_diff = diff; + } + frac++; + } + } + } + + if (best_diff == U32_MAX) + return -1; + + return 0; +} + static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset, u32 mask_on) { @@ -1678,7 +1893,10 @@ static int stm32mp1_clktree(struct udevice *dev) unsigned int clksrc[CLKSRC_NB]; unsigned int clkdiv[CLKDIV_NB]; unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; - ofnode plloff[_PLL_NB]; + unsigned int pllfracv[_PLL_NB]; + unsigned int pllcsg[_PLL_NB][PLLCSG_NB]; + bool pllcfg_valid[_PLL_NB]; + bool pllcsg_set[_PLL_NB]; int ret; int i, len; int lse_css = 0; @@ -1700,16 +1918,41 @@ static int stm32mp1_clktree(struct udevice *dev) /* check mandatory field in each pll */ for (i = 0; i < _PLL_NB; i++) { char name[12]; + ofnode node; sprintf(name, "st,pll@%d", i); - plloff[i] = dev_read_subnode(dev, name); - if (!ofnode_valid(plloff[i])) - continue; - ret = ofnode_read_u32_array(plloff[i], "cfg", - pllcfg[i], PLLCFG_NB); - if (ret < 0) { - debug("field cfg invalid: error %d\n", ret); - return -FDT_ERR_NOTFOUND; + node = dev_read_subnode(dev, name); + pllcfg_valid[i] = ofnode_valid(node); + pllcsg_set[i] = false; + if (pllcfg_valid[i]) { + debug("DT for PLL %d @ %s\n", i, name); + ret = ofnode_read_u32_array(node, "cfg", + pllcfg[i], PLLCFG_NB); + if (ret < 0) { + debug("field cfg invalid: error %d\n", ret); + return -FDT_ERR_NOTFOUND; + } + pllfracv[i] = ofnode_read_u32_default(node, "frac", 0); + + ret = ofnode_read_u32_array(node, "csg", pllcsg[i], + PLLCSG_NB); + if (!ret) { + pllcsg_set[i] = true; + } else if (ret != -FDT_ERR_NOTFOUND) { + debug("invalid csg node for pll@%d res=%d\n", + i, ret); + return ret; + } + } else if (i == _PLL1) { + /* use OPP for PLL1 for A7 CPU */ + debug("DT for PLL %d with OPP\n", i); + ret = stm32mp1_pll1_opp(priv, + clksrc[CLKSRC_PLL12], + pllcfg[i], + &pllfracv[i]); + if (ret) + return ret; + pllcfg_valid[i] = true; } } @@ -1794,29 +2037,18 @@ static int stm32mp1_clktree(struct udevice *dev) /* configure and start PLLs */ debug("configure PLLs\n"); for (i = 0; i < _PLL_NB; i++) { - u32 fracv; - u32 csg[PLLCSG_NB]; - - debug("configure PLL %d @ %d\n", i, - ofnode_to_offset(plloff[i])); - if (!ofnode_valid(plloff[i])) + if (!pllcfg_valid[i]) continue; - - fracv = ofnode_read_u32_default(plloff[i], "frac", 0); - pll_config(priv, i, pllcfg[i], fracv); - ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB); - if (!ret) { - pll_csg(priv, i, csg); - } else if (ret != -FDT_ERR_NOTFOUND) { - debug("invalid csg node for pll@%d res=%d\n", i, ret); - return ret; - } + debug("configure PLL %d\n", i); + pll_config(priv, i, pllcfg[i], pllfracv[i]); + if (pllcsg_set[i]) + pll_csg(priv, i, pllcsg[i]); pll_start(priv, i); } /* wait and start PLLs ouptut when ready */ for (i = 0; i < _PLL_NB; i++) { - if (!ofnode_valid(plloff[i])) + if (!pllcfg_valid[i]) continue; debug("output PLL %d\n", i); pll_output(priv, i, pllcfg[i][PLLCFG_O]); diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index 2b18735..db363a2 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -119,13 +119,6 @@ struct stm32_i2c_regs { #define FAST_RATE 400000 #define FAST_PLUS_RATE 1000000 -enum stm32_i2c_speed { - STM32_I2C_SPEED_STANDARD, /* 100 kHz */ - STM32_I2C_SPEED_FAST, /* 400 kHz */ - STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */ - STM32_I2C_SPEED_END, -}; - /** * struct stm32_i2c_spec - private i2c specification timing * @rate: I2C bus speed (Hz) @@ -155,7 +148,6 @@ struct stm32_i2c_spec { /** * struct stm32_i2c_setup - private I2C timing setup parameters - * @speed: I2C speed mode (standard, Fast Plus) * @speed_freq: I2C speed frequency (Hz) * @clock_src: I2C clock source frequency (Hz) * @rise_time: Rise time (ns) @@ -164,7 +156,6 @@ struct stm32_i2c_spec { * @analog_filter: Analog filter delay (On/Off) */ struct stm32_i2c_setup { - enum stm32_i2c_speed speed; u32 speed_freq; u32 clock_src; u32 rise_time; @@ -194,11 +185,12 @@ struct stm32_i2c_priv { struct stm32_i2c_regs *regs; struct clk clk; struct stm32_i2c_setup *setup; - int speed; + u32 speed; }; static const struct stm32_i2c_spec i2c_specs[] = { - [STM32_I2C_SPEED_STANDARD] = { + /* Standard speed - 100 KHz */ + { .rate = STANDARD_RATE, .rate_min = 8000, .rate_max = 120000, @@ -210,7 +202,8 @@ static const struct stm32_i2c_spec i2c_specs[] = { .l_min = 4700, .h_min = 4000, }, - [STM32_I2C_SPEED_FAST] = { + /* Fast speed - 400 KHz */ + { .rate = FAST_RATE, .rate_min = 320000, .rate_max = 480000, @@ -222,7 +215,8 @@ static const struct stm32_i2c_spec i2c_specs[] = { .l_min = 1300, .h_min = 600, }, - [STM32_I2C_SPEED_FAST_PLUS] = { + /* Fast Plus Speed - 1 MHz */ + { .rate = FAST_PLUS_RATE, .rate_min = 800000, .rate_max = 1200000, @@ -484,6 +478,7 @@ static int stm32_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, } static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, + const struct stm32_i2c_spec *specs, struct list_head *solutions) { struct stm32_i2c_timings *v; @@ -500,13 +495,13 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, af_delay_max = setup->analog_filter ? STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0; - sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time - + sdadel_min = specs->hddat_min + setup->fall_time - af_delay_min - (setup->dnf + 3) * i2cclk; - sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time - + sdadel_max = specs->vddat_max - setup->rise_time - af_delay_max - (setup->dnf + 4) * i2cclk; - scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min; + scldel_min = setup->rise_time + specs->sudat_min; if (sdadel_min < 0) sdadel_min = 0; @@ -558,6 +553,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, } static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup, + const struct stm32_i2c_spec *specs, struct list_head *solutions, struct stm32_i2c_timings *s) { @@ -580,8 +576,8 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup, dnf_delay = setup->dnf * i2cclk; tsync = af_delay_min + dnf_delay + (2 * i2cclk); - clk_max = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_min; - clk_min = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_max; + clk_max = STM32_NSEC_PER_SEC / specs->rate_min; + clk_min = STM32_NSEC_PER_SEC / specs->rate_max; /* * Among Prescaler possibilities discovered above figures out SCL Low @@ -599,7 +595,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup, for (l = 0; l < STM32_SCLL_MAX; l++) { u32 tscl_l = (l + 1) * prescaler + tsync; - if ((tscl_l < i2c_specs[setup->speed].l_min) || + if (tscl_l < specs->l_min || (i2cclk >= ((tscl_l - af_delay_min - dnf_delay) / 4))) { continue; @@ -611,7 +607,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup, setup->rise_time + setup->fall_time; if ((tscl >= clk_min) && (tscl <= clk_max) && - (tscl_h >= i2c_specs[setup->speed].h_min) && + (tscl_h >= specs->h_min) && (i2cclk < tscl_h)) { u32 clk_error; @@ -640,26 +636,40 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup, return ret; } +static const struct stm32_i2c_spec *get_specs(u32 rate) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(i2c_specs); i++) + if (rate <= i2c_specs[i].rate) + return &i2c_specs[i]; + + /* NOT REACHED */ + return ERR_PTR(-EINVAL); +} + static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv, struct stm32_i2c_setup *setup, struct stm32_i2c_timings *output) { + const struct stm32_i2c_spec *specs; struct stm32_i2c_timings *v, *_v; struct list_head solutions; int ret; - if (setup->speed >= STM32_I2C_SPEED_END) { - pr_err("%s: speed out of bound {%d/%d}\n", __func__, - setup->speed, STM32_I2C_SPEED_END - 1); + specs = get_specs(setup->speed_freq); + if (specs == ERR_PTR(-EINVAL)) { + pr_err("%s: speed out of bound {%d}\n", __func__, + setup->speed_freq); return -EINVAL; } - if ((setup->rise_time > i2c_specs[setup->speed].rise_max) || - (setup->fall_time > i2c_specs[setup->speed].fall_max)) { + if (setup->rise_time > specs->rise_max || + setup->fall_time > specs->fall_max) { pr_err("%s :timings out of bound Rise{%d>%d}/Fall{%d>%d}\n", __func__, - setup->rise_time, i2c_specs[setup->speed].rise_max, - setup->fall_time, i2c_specs[setup->speed].fall_max); + setup->rise_time, specs->rise_max, + setup->fall_time, specs->fall_max); return -EINVAL; } @@ -669,18 +679,12 @@ static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv, return -EINVAL; } - if (setup->speed_freq > i2c_specs[setup->speed].rate) { - pr_err("%s: Freq {%d/%d}\n", __func__, - setup->speed_freq, i2c_specs[setup->speed].rate); - return -EINVAL; - } - INIT_LIST_HEAD(&solutions); - ret = stm32_i2c_compute_solutions(setup, &solutions); + ret = stm32_i2c_compute_solutions(setup, specs, &solutions); if (ret) goto exit; - ret = stm32_i2c_choose_solution(setup, &solutions, output); + ret = stm32_i2c_choose_solution(setup, specs, &solutions, output); if (ret) goto exit; @@ -699,14 +703,24 @@ exit: return ret; } +static u32 get_lower_rate(u32 rate) +{ + int i; + + for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--) + if (rate > i2c_specs[i].rate) + return i2c_specs[i].rate; + + return i2c_specs[0].rate; +} + static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv, struct stm32_i2c_timings *timing) { struct stm32_i2c_setup *setup = i2c_priv->setup; int ret = 0; - setup->speed = i2c_priv->speed; - setup->speed_freq = i2c_specs[setup->speed].rate; + setup->speed_freq = i2c_priv->speed; setup->clock_src = clk_get_rate(&i2c_priv->clk); if (!setup->clock_src) { @@ -719,13 +733,11 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv, if (ret) { debug("%s: failed to compute I2C timings.\n", __func__); - if (i2c_priv->speed > STM32_I2C_SPEED_STANDARD) { - i2c_priv->speed--; - setup->speed = i2c_priv->speed; + if (setup->speed_freq > STANDARD_RATE) { setup->speed_freq = - i2c_specs[setup->speed].rate; + get_lower_rate(setup->speed_freq); debug("%s: downgrade I2C Speed Freq to (%i)\n", - __func__, i2c_specs[setup->speed].rate); + __func__, setup->speed_freq); } else { break; } @@ -737,13 +749,15 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv, return ret; } - debug("%s: I2C Speed(%i), Freq(%i), Clk Source(%i)\n", __func__, - setup->speed, setup->speed_freq, setup->clock_src); + debug("%s: I2C Freq(%i), Clk Source(%i)\n", __func__, + setup->speed_freq, setup->clock_src); debug("%s: I2C Rise(%i) and Fall(%i) Time\n", __func__, setup->rise_time, setup->fall_time); debug("%s: I2C Analog Filter(%s), DNF(%i)\n", __func__, setup->analog_filter ? "On" : "Off", setup->dnf); + i2c_priv->speed = setup->speed_freq; + return 0; } @@ -783,21 +797,13 @@ static int stm32_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) { struct stm32_i2c_priv *i2c_priv = dev_get_priv(bus); - switch (speed) { - case STANDARD_RATE: - i2c_priv->speed = STM32_I2C_SPEED_STANDARD; - break; - case FAST_RATE: - i2c_priv->speed = STM32_I2C_SPEED_FAST; - break; - case FAST_PLUS_RATE: - i2c_priv->speed = STM32_I2C_SPEED_FAST_PLUS; - break; - default: + if (speed > FAST_PLUS_RATE) { debug("%s: Speed %d not supported\n", __func__, speed); return -EINVAL; } + i2c_priv->speed = speed; + return stm32_i2c_hw_config(i2c_priv); } diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index f73f072..79ced50 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -120,6 +120,20 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode) } #endif +int dm_mmc_host_power_cycle(struct udevice *dev) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (ops->host_power_cycle) + return ops->host_power_cycle(dev); + return 0; +} + +int mmc_host_power_cycle(struct mmc *mmc) +{ + return dm_mmc_host_power_cycle(mmc->dev); +} + int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) { int val; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 585951c..826565f 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1486,6 +1486,16 @@ static int mmc_set_ios(struct mmc *mmc) return ret; } + +static int mmc_host_power_cycle(struct mmc *mmc) +{ + int ret = 0; + + if (mmc->cfg->ops->host_power_cycle) + ret = mmc->cfg->ops->host_power_cycle(mmc); + + return ret; +} #endif int mmc_set_clock(struct mmc *mmc, uint clock, bool disable) @@ -2566,6 +2576,11 @@ static int mmc_power_cycle(struct mmc *mmc) ret = mmc_power_off(mmc); if (ret) return ret; + + ret = mmc_host_power_cycle(mmc); + if (ret) + return ret; + /* * SD spec recommends at least 1ms of delay. Let's wait for 2ms * to be on the safer side. diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c index 32434a4..1726ed7 100644 --- a/drivers/mmc/stm32_sdmmc2.c +++ b/drivers/mmc/stm32_sdmmc2.c @@ -524,8 +524,6 @@ static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_priv *priv) return; stm32_sdmmc2_reset(priv); - writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk, - priv->base + SDMMC_POWER); } /* @@ -619,10 +617,21 @@ static int stm32_sdmmc2_getcd(struct udevice *dev) return 1; } +static int stm32_sdmmc2_host_power_cycle(struct udevice *dev) +{ + struct stm32_sdmmc2_priv *priv = dev_get_priv(dev); + + writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk, + priv->base + SDMMC_POWER); + + return 0; +} + static const struct dm_mmc_ops stm32_sdmmc2_ops = { .send_cmd = stm32_sdmmc2_send_cmd, .set_ios = stm32_sdmmc2_set_ios, .get_cd = stm32_sdmmc2_getcd, + .host_power_cycle = stm32_sdmmc2_host_power_cycle, }; static int stm32_sdmmc2_probe(struct udevice *dev) diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 5b933fb..d8887c1 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -692,6 +692,30 @@ static int eqos_start_resets_tegra186(struct udevice *dev) static int eqos_start_resets_stm32(struct udevice *dev) { + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) { + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1); + if (ret < 0) { + pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", + ret); + return ret; + } + + udelay(2); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); + if (ret < 0) { + pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", + ret); + return ret; + } + } + + debug("%s: OK\n", __func__); return 0; } @@ -707,6 +731,11 @@ static int eqos_stop_resets_tegra186(struct udevice *dev) static int eqos_stop_resets_stm32(struct udevice *dev) { + struct eqos_priv *eqos = dev_get_priv(dev); + + if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) + dm_gpio_set_value(&eqos->phy_reset_gpio, 1); + return 0; } @@ -1604,7 +1633,7 @@ static int eqos_probe_resources_stm32(struct udevice *dev) int interface; bool eth_clk_sel_reg = false; bool eth_ref_clk_sel_reg = false; - + struct ofnode_phandle_args phandle_args; debug("%s(dev=%p):\n", __func__, dev); @@ -1650,6 +1679,19 @@ static int eqos_probe_resources_stm32(struct udevice *dev) if (ret) pr_warn("No phy clock provided %d", ret); + /* search "reset-gpios" in phy node */ + ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, + &phandle_args); + if (!ret) { + ret = gpio_request_by_name_nodev(phandle_args.node, + "reset-gpios", 0, + &eqos->phy_reset_gpio, + GPIOD_IS_OUT | + GPIOD_IS_OUT_ACTIVE); + } + if (ret) + pr_warn("gpio_request_by_name(phy reset) not provided %d", ret); + debug("%s: OK\n", __func__); return 0; @@ -1713,6 +1755,9 @@ static int eqos_remove_resources_stm32(struct udevice *dev) if (clk_valid(&eqos->clk_ck)) clk_free(&eqos->clk_ck); + if (dm_gpio_is_valid(&eqos->phy_reset_gpio)) + dm_gpio_free(dev, &eqos->phy_reset_gpio); + debug("%s: OK\n", __func__); return 0; } diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c index f6484da..c87882d 100644 --- a/drivers/ram/stm32mp1/stm32mp1_ddr.c +++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c @@ -855,14 +855,34 @@ void stm32mp1_ddr_init(struct ddr_info *priv, { u32 pir; int ret = -EINVAL; + char bus_width; + + switch (config->c_reg.mstr & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK) { + case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER: + bus_width = 8; + break; + case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF: + bus_width = 16; + break; + default: + bus_width = 32; + break; + } + if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3) ret = board_ddr_power_init(STM32MP_DDR3); - else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2) - ret = board_ddr_power_init(STM32MP_LPDDR2); - else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR3) - ret = board_ddr_power_init(STM32MP_LPDDR3); - + else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2) { + if (bus_width == 32) + ret = board_ddr_power_init(STM32MP_LPDDR2_32); + else + ret = board_ddr_power_init(STM32MP_LPDDR2_16); + } else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR3) { + if (bus_width == 32) + ret = board_ddr_power_init(STM32MP_LPDDR3_32); + else + ret = board_ddr_power_init(STM32MP_LPDDR3_16); + } if (ret) panic("ddr power init failed\n"); diff --git a/drivers/ram/stm32mp1/stm32mp1_interactive.c b/drivers/ram/stm32mp1/stm32mp1_interactive.c index 8df6a39..62da596 100644 --- a/drivers/ram/stm32mp1/stm32mp1_interactive.c +++ b/drivers/ram/stm32mp1/stm32mp1_interactive.c @@ -367,7 +367,6 @@ bool stm32mp1_ddr_interactive(void *priv, enum stm32mp1_ddr_interact_step step, const struct stm32mp1_ddr_config *config) { - const char *prompt = "DDR>"; char buffer[CONFIG_SYS_CBSIZE]; char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ int argc; @@ -403,13 +402,12 @@ bool stm32mp1_ddr_interactive(void *priv, } printf("%d:%s\n", step, step_str[step]); - printf("%s\n", prompt); if (next_step > step) return false; while (next_step == step) { - cli_readline_into_buffer(prompt, buffer, 0); + cli_readline_into_buffer("DDR>", buffer, 0); argc = cli_simple_parse_line(buffer, argv); if (!argc) continue; diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c index 36f1e97..89eed8c 100644 --- a/drivers/remoteproc/rproc-uclass.c +++ b/drivers/remoteproc/rproc-uclass.c @@ -244,6 +244,7 @@ UCLASS_DRIVER(rproc) = { .flags = DM_UC_FLAG_SEQ_ALIAS, .pre_probe = rproc_pre_probe, .post_probe = rproc_post_probe, + .per_device_auto_alloc_size = sizeof(struct rproc_priv), .per_device_platdata_auto_alloc_size = sizeof(struct dm_rproc_uclass_pdata), }; @@ -432,7 +433,7 @@ static int rproc_load_elf_image(struct udevice *dev, unsigned long addr, } /* Load each program header */ - for (i = 0; i < ehdr->e_phnum; ++i) { + for (i = 0; i < ehdr->e_phnum; ++i, phdr++) { void *dst = (void *)(uintptr_t)phdr->p_paddr; void *src = (void *)addr + phdr->p_offset; @@ -453,7 +454,6 @@ static int rproc_load_elf_image(struct udevice *dev, unsigned long addr, roundup((unsigned long)dst + phdr->p_filesz, ARCH_DMA_MINALIGN) - rounddown((unsigned long)dst, ARCH_DMA_MINALIGN)); - ++phdr; } *entry = ehdr->e_entry; @@ -625,6 +625,7 @@ int rproc_load_rsc_table(int id, ulong addr, ulong size, ulong *rsc_addr, { struct udevice *dev = NULL; const struct dm_rproc_ops *ops; + struct rproc_priv *priv; int ret; ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); @@ -640,23 +641,27 @@ int rproc_load_rsc_table(int id, ulong addr, ulong size, ulong *rsc_addr, return -EINVAL; } - dev_dbg(dev, "Loocking for resource table from address 0x%08lX size of %lu bytes\n", + dev_dbg(dev, "Looking for resource table from address 0x%08lX size of %lu bytes\n", addr, size); - if (!rproc_elf_sanity_check(dev, addr, size)) { - /* load elf image */ - ret = rproc_elf_find_load_rsc_table(dev, addr, size, rsc_addr, - rsc_size); - if (ret) { - dev_dbg(dev, "No resource table found\n"); - return -ENODATA; - } - dev_dbg(dev, "Resource table at 0x%08lx, size 0x%x!\n", - *rsc_addr, *rsc_size); - return 0; + ret = rproc_elf_sanity_check(dev, addr, size); + if (ret) + return ret; + + ret = rproc_elf_find_load_rsc_table(dev, addr, size, rsc_addr, + rsc_size); + if (ret) { + dev_dbg(dev, "No resource table found\n"); + return -ENODATA; } - return -ENODATA; + priv = dev_get_uclass_priv(dev); + priv->rsc_table_addr = *rsc_addr; + priv->rsc_table_size = *rsc_size; + dev_dbg(dev, "Resource table at 0x%08lx, size 0x%x!\n", + priv->rsc_table_addr, priv->rsc_table_size); + + return 0; }; /* diff --git a/drivers/remoteproc/stm32_copro.c b/drivers/remoteproc/stm32_copro.c index 0a8b900..9fed8c8 100644 --- a/drivers/remoteproc/stm32_copro.c +++ b/drivers/remoteproc/stm32_copro.c @@ -12,6 +12,7 @@ #include #include #include +#include #define RCC_GCR_HOLD_BOOT 0 #define RCC_GCR_RELEASE_BOOT 1 @@ -202,6 +203,7 @@ static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size) */ static int stm32_copro_start(struct udevice *dev) { + struct rproc_priv *uc_priv = dev_get_uclass_priv(dev); int ret; /* move hold boot from true to false start the copro */ @@ -213,7 +215,12 @@ static int stm32_copro_start(struct udevice *dev) * Once copro running, reset hold boot flag to avoid copro * rebooting autonomously */ - return stm32_copro_set_hold_boot(dev, true); + ret = stm32_copro_set_hold_boot(dev, true); + if (!ret) + /* Store rsc_address in bkp register */ + writel(uc_priv->rsc_table_addr, TAMP_COPRO_RSC_TBL_ADDRESS); + + return ret; } /** diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8809552..890212de 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -74,7 +74,8 @@ config VIDEO_ANSI console. config VIDEO_MIPI_DSI - bool + bool "Support MIPI DSI interface" + depends on DM_VIDEO help Support MIPI DSI interface for driving a MIPI compatible device. The MIPI Display Serial Interface (MIPI DSI) defines a high-speed @@ -332,16 +333,18 @@ config VIDEO_LCD_ORISETECH_OTM8009A depends on DM_VIDEO select VIDEO_MIPI_DSI default n - ---help--- - Support for Orise Tech otm8009a 480p dsi 2dl video mode panel. + help + Say Y here if you want to enable support for Orise Technology + otm8009a 480x800 dsi 2dl panel. config VIDEO_LCD_RAYDIUM_RM68200 bool "RM68200 DSI LCD panel support" depends on DM_VIDEO select VIDEO_MIPI_DSI default n - ---help--- - Support for Raydium rm68200 720x1280 dsi 2dl video mode panel. + help + Say Y here if you want to enable support for Raydium RM68200 + 720x1280 DSI video mode panel. config VIDEO_LCD_SSD2828 bool "SSD2828 bridge chip" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index c3d39ed..796eb6c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_CONSOLE_ROTATION) += console_rotate.o obj-$(CONFIG_CONSOLE_TRUETYPE) += console_truetype.o fonts/ obj-$(CONFIG_DISPLAY) += display-uclass.o obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o +obj-$(CONFIG_DM_VIDEO) += dsi-host-uclass.o obj-$(CONFIG_DM_VIDEO) += panel-uclass.o simple_panel.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o obj-$(CONFIG_DM_VIDEO) += video_bmp.o @@ -44,6 +45,7 @@ obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o +obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o obj-$(CONFIG_VIDEO_EFI) += efi.o obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.o obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o @@ -54,6 +56,7 @@ obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o +obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o @@ -63,8 +66,6 @@ obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o obj-$(CONFIG_VIDEO_VESA) += vesa.o -obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o -obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_display.o obj-y += bridge/ obj-y += sunxi/ diff --git a/drivers/video/dsi-host-uclass.c b/drivers/video/dsi-host-uclass.c new file mode 100644 index 0000000..1db1f88 --- /dev/null +++ b/drivers/video/dsi-host-uclass.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved + * Author(s): Yannick Fertre for STMicroelectronics. + * + */ + +#include +#include +#include + +int dsi_host_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops) +{ + struct dsi_host_ops *ops = dsi_host_get_ops(dev); + + if (!ops->init) + return -ENOSYS; + + return ops->init(dev, device, timings, max_data_lanes, phy_ops); +} + +int dsi_host_enable(struct udevice *dev) +{ + struct dsi_host_ops *ops = dsi_host_get_ops(dev); + + if (!ops->enable) + return -ENOSYS; + + return ops->enable(dev); +} + +UCLASS_DRIVER(dsi_host) = { + .id = UCLASS_DSI_HOST, + .name = "dsi_host", +}; diff --git a/drivers/video/dw_mipi_dsi.c b/drivers/video/dw_mipi_dsi.c index fe1e25a..83d7c7b 100644 --- a/drivers/video/dw_mipi_dsi.c +++ b/drivers/video/dw_mipi_dsi.c @@ -1,25 +1,24 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd - * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved * Author(s): Philippe Cornu for STMicroelectronics. * Yannick Fertre for STMicroelectronics. * - * This generic Synopsys DesignWare MIPI DSI host driver is based on the - * Linux Kernel driver from drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c. + * This generic Synopsys DesignWare MIPI DSI host driver is inspired from + * the Linux Kernel driver drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c. */ #include #include +#include #include #include -#include #include #include #include #include #include -#include #include #include @@ -221,11 +220,8 @@ struct dw_mipi_dsi { void __iomem *base; unsigned int lane_mbps; /* per lane */ u32 channel; - u32 lanes; - u32 format; - unsigned long mode_flags; unsigned int max_data_lanes; - const struct dw_mipi_dsi_phy_ops *phy_ops; + const struct mipi_dsi_phy_ops *phy_ops; }; static int dsi_mode_vrefresh(struct display_timing *timings) @@ -286,10 +282,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, return -EINVAL; } - dsi->lanes = device->lanes; dsi->channel = device->channel; - dsi->format = device->format; - dsi->mode_flags = device->mode_flags; return 0; } @@ -444,6 +437,7 @@ static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = { static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) { + struct mipi_dsi_device *device = dsi->device; u32 val; /* @@ -453,9 +447,9 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) */ val = ENABLE_LOW_POWER; - if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) + if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) val |= VID_MODE_TYPE_BURST; - else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + else if (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES; else val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; @@ -466,6 +460,8 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi, unsigned long mode_flags) { + const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops; + dsi_write(dsi, DSI_PWR_UP, RESET); if (mode_flags & MIPI_DSI_MODE_VIDEO) { @@ -476,10 +472,13 @@ static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi, dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); } + if (phy_ops->post_set_mode) + phy_ops->post_set_mode(dsi->device, mode_flags); + dsi_write(dsi, DSI_PWR_UP, POWERUP); } -static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) +static void dw_mipi_dsi_init_pll(struct dw_mipi_dsi *dsi) { /* * The maximum permitted escape clock is 20MHz and it is derived from @@ -505,9 +504,10 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi, struct display_timing *timings) { + struct mipi_dsi_device *device = dsi->device; u32 val = 0, color = 0; - switch (dsi->format) { + switch (device->format) { case MIPI_DSI_FMT_RGB888: color = DPI_COLOR_CODING_24BIT; break; @@ -522,9 +522,9 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi, break; } - if (dsi->mode_flags & DISPLAY_FLAGS_VSYNC_HIGH) + if (device->mode_flags & DISPLAY_FLAGS_VSYNC_HIGH) val |= VSYNC_ACTIVE_LOW; - if (dsi->mode_flags & DISPLAY_FLAGS_HSYNC_HIGH) + if (device->mode_flags & DISPLAY_FLAGS_HSYNC_HIGH) val |= HSYNC_ACTIVE_LOW; dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel)); @@ -560,6 +560,8 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi, static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) { + const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops; + /* * TODO dw drv improvements * compute high speed transmission counter timeout according @@ -573,6 +575,9 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) */ dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00); dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); + + if (phy_ops->post_set_mode) + phy_ops->post_set_mode(dsi->device, 0); } /* Get lane byte clock cycles. */ @@ -662,13 +667,15 @@ static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi) { + struct mipi_dsi_device *device = dsi->device; + /* * TODO dw drv improvements * stop wait time should be the maximum between host dsi * and panel stop wait times */ dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) | - N_LANES(dsi->lanes)); + N_LANES(device->lanes)); } static void dw_mipi_dsi_dphy_init(struct dw_mipi_dsi *dsi) @@ -712,15 +719,16 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_bridge_set(struct dw_mipi_dsi *dsi, struct display_timing *timings) { - const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->phy_ops; + const struct mipi_dsi_phy_ops *phy_ops = dsi->phy_ops; + struct mipi_dsi_device *device = dsi->device; int ret; - ret = phy_ops->get_lane_mbps(dsi->device, timings, dsi->lanes, - dsi->format, &dsi->lane_mbps); + ret = phy_ops->get_lane_mbps(dsi->device, timings, device->lanes, + device->format, &dsi->lane_mbps); if (ret) dev_warn(dsi->dev, "Phy get_lane_mbps() failed\n"); - dw_mipi_dsi_init(dsi); + dw_mipi_dsi_init_pll(dsi); dw_mipi_dsi_dpi_config(dsi, timings); dw_mipi_dsi_packet_handler_config(dsi); dw_mipi_dsi_video_mode_config(dsi); @@ -747,62 +755,33 @@ static void dw_mipi_dsi_bridge_set(struct dw_mipi_dsi *dsi, dw_mipi_dsi_set_mode(dsi, 0); } -void dw_mipi_dsi_bridge_enable(struct mipi_dsi_device *device) +static int dw_mipi_dsi_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops) { - struct mipi_dsi_host *host = device->host; - struct dw_mipi_dsi *dsi = host_to_dsi(host); - - /* Switch to video mode for panel-bridge enable & panel enable */ - dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); -} -EXPORT_SYMBOL_GPL(dw_mipi_dsi_bridge_enable); - -int dw_mipi_dsi_init_bridge(struct mipi_dsi_device *device) -{ - struct dw_mipi_dsi_plat_data *platdata = dev_get_platdata(device->dev); - struct udevice *panel = platdata->panel; - struct display_timing timings; - struct dw_mipi_dsi *dsi; + struct dw_mipi_dsi *dsi = dev_get_priv(dev); struct clk clk; int ret; - dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); + if (!phy_ops->init || !phy_ops->get_lane_mbps) { + dev_err(device->dev, "Phy not properly configured\n"); + return -ENODEV; + } - dsi->phy_ops = platdata->phy_ops; - dsi->max_data_lanes = platdata->max_data_lanes; + dsi->phy_ops = phy_ops; + dsi->max_data_lanes = max_data_lanes; dsi->device = device; dsi->dsi_host.ops = &dw_mipi_dsi_host_ops; device->host = &dsi->dsi_host; - /* TODO Get these settings from panel */ - dsi->lanes = 2; - dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | - MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_LPM; - dsi->base = (void *)dev_read_addr(device->dev); if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) { dev_err(device->dev, "dsi dt register address error\n"); return -EINVAL; } - ret = panel_get_display_timing(panel, &timings); - if (ret) { - ret = fdtdec_decode_display_timing(gd->fdt_blob, - dev_of_offset(panel), - 0, &timings); - if (ret) { - dev_err(dev, "decode display timing error %d\n", ret); - return ret; - } - } - - if (!dsi->phy_ops->init || !dsi->phy_ops->get_lane_mbps) { - dev_err(device->dev, "Phy not properly configured\n"); - return -ENODEV; - } - ret = clk_get_by_name(device->dev, "px_clk", &clk); if (ret) { dev_err(device->dev, "peripheral clock get error %d\n", ret); @@ -810,13 +789,40 @@ int dw_mipi_dsi_init_bridge(struct mipi_dsi_device *device) } /* get the pixel clock set by the clock framework */ - timings.pixelclock.typ = clk_get_rate(&clk); + timings->pixelclock.typ = clk_get_rate(&clk); - dw_mipi_dsi_bridge_set(dsi, &timings); + dw_mipi_dsi_bridge_set(dsi, timings); return 0; } -EXPORT_SYMBOL_GPL(dw_mipi_dsi_init_bridge); + +static int dw_mipi_dsi_enable(struct udevice *dev) +{ + struct dw_mipi_dsi *dsi = dev_get_priv(dev); + + /* Switch to video mode for panel-bridge enable & panel enable */ + dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); + + return 0; +} + +struct dsi_host_ops dw_mipi_dsi_ops = { + .init = dw_mipi_dsi_init, + .enable = dw_mipi_dsi_enable, +}; + +static int dw_mipi_dsi_probe(struct udevice *dev) +{ + return 0; +} + +U_BOOT_DRIVER(dw_mipi_dsi) = { + .name = "dw_mipi_dsi", + .id = UCLASS_DSI_HOST, + .probe = dw_mipi_dsi_probe, + .ops = &dw_mipi_dsi_ops, + .priv_auto_alloc_size = sizeof(struct dw_mipi_dsi), +}; MODULE_AUTHOR("Chris Zhong "); MODULE_AUTHOR("Philippe Cornu "); diff --git a/drivers/video/mipi_display.c b/drivers/video/mipi_display.c deleted file mode 100644 index 611ee53..0000000 --- a/drivers/video/mipi_display.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * MIPI DSI Bus - * - * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. - * Copyright (C) 2018 STMicroelectronics - All Rights Reserved - * Andrzej Hajda - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Mipi_display.c contains a set of dsi helpers. - * This file is based on the drm helper file drivers/gpu/drm/drm_mipi_dsi.c - * (kernel linux). - * - */ - -#include -#include -#include -#include -#include - -/** - * DOC: dsi helpers - * - * These functions contain some common logic and helpers to deal with MIPI DSI - * peripherals. - * - * Helpers are provided for a number of standard MIPI DSI command as well as a - * subset of the MIPI DCS command set. - */ - -/** - * mipi_dsi_attach - attach a DSI device to its DSI host - * @dsi: DSI peripheral - */ -int mipi_dsi_attach(struct mipi_dsi_device *dsi) -{ - const struct mipi_dsi_host_ops *ops = dsi->host->ops; - - if (!ops || !ops->attach) - return -ENOSYS; - - return ops->attach(dsi->host, dsi); -} -EXPORT_SYMBOL(mipi_dsi_attach); - -/** - * mipi_dsi_detach - detach a DSI device from its DSI host - * @dsi: DSI peripheral - */ -int mipi_dsi_detach(struct mipi_dsi_device *dsi) -{ - const struct mipi_dsi_host_ops *ops = dsi->host->ops; - - if (!ops || !ops->detach) - return -ENOSYS; - - return ops->detach(dsi->host, dsi); -} -EXPORT_SYMBOL(mipi_dsi_detach); - -/** - * mipi_dsi_device_transfer - transfer message to a DSI device - * @dsi: DSI peripheral - * @msg: message - */ -static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi, - struct mipi_dsi_msg *msg) -{ - const struct mipi_dsi_host_ops *ops = dsi->host->ops; - - if (!ops || !ops->transfer) - return -ENOSYS; - - if (dsi->mode_flags & MIPI_DSI_MODE_LPM) - msg->flags |= MIPI_DSI_MSG_USE_LPM; - - return ops->transfer(dsi->host, msg); -} - -/** - * mipi_dsi_packet_format_is_short - check if a packet is of the short format - * @type: MIPI DSI data type of the packet - * - * Return: true if the packet for the given data type is a short packet, false - * otherwise. - */ -bool mipi_dsi_packet_format_is_short(u8 type) -{ - switch (type) { - case MIPI_DSI_V_SYNC_START: - case MIPI_DSI_V_SYNC_END: - case MIPI_DSI_H_SYNC_START: - case MIPI_DSI_H_SYNC_END: - case MIPI_DSI_END_OF_TRANSMISSION: - case MIPI_DSI_COLOR_MODE_OFF: - case MIPI_DSI_COLOR_MODE_ON: - case MIPI_DSI_SHUTDOWN_PERIPHERAL: - case MIPI_DSI_TURN_ON_PERIPHERAL: - case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: - case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: - case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: - case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: - case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: - case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: - case MIPI_DSI_DCS_SHORT_WRITE: - case MIPI_DSI_DCS_SHORT_WRITE_PARAM: - case MIPI_DSI_DCS_READ: - case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: - return true; - } - - return false; -} -EXPORT_SYMBOL(mipi_dsi_packet_format_is_short); - -/** - * mipi_dsi_packet_format_is_long - check if a packet is of the long format - * @type: MIPI DSI data type of the packet - * - * Return: true if the packet for the given data type is a long packet, false - * otherwise. - */ -bool mipi_dsi_packet_format_is_long(u8 type) -{ - switch (type) { - case MIPI_DSI_NULL_PACKET: - case MIPI_DSI_BLANKING_PACKET: - case MIPI_DSI_GENERIC_LONG_WRITE: - case MIPI_DSI_DCS_LONG_WRITE: - case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20: - case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24: - case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16: - case MIPI_DSI_PACKED_PIXEL_STREAM_30: - case MIPI_DSI_PACKED_PIXEL_STREAM_36: - case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12: - case MIPI_DSI_PACKED_PIXEL_STREAM_16: - case MIPI_DSI_PACKED_PIXEL_STREAM_18: - case MIPI_DSI_PIXEL_STREAM_3BYTE_18: - case MIPI_DSI_PACKED_PIXEL_STREAM_24: - return true; - } - - return false; -} -EXPORT_SYMBOL(mipi_dsi_packet_format_is_long); - -/** - * mipi_dsi_create_packet - create a packet from a message according to the - * DSI protocol - * @packet: pointer to a DSI packet structure - * @msg: message to translate into a packet - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_create_packet(struct mipi_dsi_packet *packet, - const struct mipi_dsi_msg *msg) -{ - if (!packet || !msg) - return -EINVAL; - - /* do some minimum sanity checking */ - if (!mipi_dsi_packet_format_is_short(msg->type) && - !mipi_dsi_packet_format_is_long(msg->type)) - return -EINVAL; - - if (msg->channel > 3) - return -EINVAL; - - memset(packet, 0, sizeof(*packet)); - packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f); - - /* TODO: compute ECC if hardware support is not available */ - - /* - * Long write packets contain the word count in header bytes 1 and 2. - * The payload follows the header and is word count bytes long. - * - * Short write packets encode up to two parameters in header bytes 1 - * and 2. - */ - if (mipi_dsi_packet_format_is_long(msg->type)) { - packet->header[1] = (msg->tx_len >> 0) & 0xff; - packet->header[2] = (msg->tx_len >> 8) & 0xff; - - packet->payload_length = msg->tx_len; - packet->payload = msg->tx_buf; - } else { - const u8 *tx = msg->tx_buf; - - packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0; - packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0; - } - - packet->size = sizeof(packet->header) + packet->payload_length; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_create_packet); - -/* - * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the - * the payload in a long packet transmitted from the peripheral back to the - * host processor - * @dsi: DSI peripheral device - * @value: the maximum size of the payload - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi, - u16 value) -{ - u8 tx[2] = { value & 0xff, value >> 8 }; - struct mipi_dsi_msg msg = { - .channel = dsi->channel, - .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, - .tx_len = sizeof(tx), - .tx_buf = tx, - }; - int ret = mipi_dsi_device_transfer(dsi, &msg); - - return (ret < 0) ? ret : 0; -} -EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size); - -/** - * mipi_dsi_generic_write() - transmit data using a generic write packet - * @dsi: DSI peripheral device - * @payload: buffer containing the payload - * @size: size of payload buffer - * - * This function will automatically choose the right data type depending on - * the payload length. - * - * Return: The number of bytes transmitted on success or a negative error code - * on failure. - */ -ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload, - size_t size) -{ - struct mipi_dsi_msg msg = { - .channel = dsi->channel, - .tx_buf = payload, - .tx_len = size - }; - - switch (size) { - case 0: - msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM; - break; - - case 1: - msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM; - break; - - case 2: - msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM; - break; - - default: - msg.type = MIPI_DSI_GENERIC_LONG_WRITE; - break; - } - - return mipi_dsi_device_transfer(dsi, &msg); -} -EXPORT_SYMBOL(mipi_dsi_generic_write); - -/** - * mipi_dsi_generic_read() - receive data using a generic read packet - * @dsi: DSI peripheral device - * @params: buffer containing the request parameters - * @num_params: number of request parameters - * @data: buffer in which to return the received data - * @size: size of receive buffer - * - * This function will automatically choose the right data type depending on - * the number of parameters passed in. - * - * Return: The number of bytes successfully read or a negative error code on - * failure. - */ -ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params, - size_t num_params, void *data, size_t size) -{ - struct mipi_dsi_msg msg = { - .channel = dsi->channel, - .tx_len = num_params, - .tx_buf = params, - .rx_len = size, - .rx_buf = data - }; - - switch (num_params) { - case 0: - msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM; - break; - - case 1: - msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM; - break; - - case 2: - msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM; - break; - - default: - return -EINVAL; - } - - return mipi_dsi_device_transfer(dsi, &msg); -} -EXPORT_SYMBOL(mipi_dsi_generic_read); - -/** - * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload - * @dsi: DSI peripheral device - * @data: buffer containing data to be transmitted - * @len: size of transmission buffer - * - * This function will automatically choose the right data type depending on - * the command payload length. - * - * Return: The number of bytes successfully transmitted or a negative error - * code on failure. - */ -ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, - const void *data, size_t len) -{ - struct mipi_dsi_msg msg = { - .channel = dsi->channel, - .tx_buf = data, - .tx_len = len - }; - - switch (len) { - case 0: - return -EINVAL; - - case 1: - msg.type = MIPI_DSI_DCS_SHORT_WRITE; - break; - - case 2: - msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; - break; - - default: - msg.type = MIPI_DSI_DCS_LONG_WRITE; - break; - } - - return mipi_dsi_device_transfer(dsi, &msg); -} -EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer); - -/** - * mipi_dsi_dcs_write() - send DCS write command - * @dsi: DSI peripheral device - * @cmd: DCS command - * @data: buffer containing the command payload - * @len: command payload length - * - * This function will automatically choose the right data type depending on - * the command payload length. - * - * Return: The number of bytes successfully transmitted or a negative error - * code on failure. - */ -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, - const void *data, size_t len) -{ - ssize_t err; - size_t size; - u8 *tx; - - if (len > 0) { - size = 1 + len; - - tx = kmalloc(size, GFP_KERNEL); - if (!tx) - return -ENOMEM; - - /* concatenate the DCS command byte and the payload */ - tx[0] = cmd; - memcpy(&tx[1], data, len); - } else { - tx = &cmd; - size = 1; - } - - err = mipi_dsi_dcs_write_buffer(dsi, tx, size); - - if (len > 0) - kfree(tx); - - return err; -} -EXPORT_SYMBOL(mipi_dsi_dcs_write); - -/** - * mipi_dsi_dcs_read() - send DCS read request command - * @dsi: DSI peripheral device - * @cmd: DCS command - * @data: buffer in which to receive data - * @len: size of receive buffer - * - * Return: The number of bytes read or a negative error code on failure. - */ -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, - size_t len) -{ - struct mipi_dsi_msg msg = { - .channel = dsi->channel, - .type = MIPI_DSI_DCS_READ, - .tx_buf = &cmd, - .tx_len = 1, - .rx_buf = data, - .rx_len = len - }; - - return mipi_dsi_device_transfer(dsi, &msg); -} -EXPORT_SYMBOL(mipi_dsi_dcs_read); - -/** - * mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any - * given pixel format defined by the MIPI DSI - * specification - * @fmt: MIPI DSI pixel format - * - * Returns: The number of bits per pixel of the given pixel format. - */ -int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) -{ - switch (fmt) { - case MIPI_DSI_FMT_RGB888: - case MIPI_DSI_FMT_RGB666: - return 24; - - case MIPI_DSI_FMT_RGB666_PACKED: - return 18; - - case MIPI_DSI_FMT_RGB565: - return 16; - } - - return -EINVAL; -} -EXPORT_SYMBOL(mipi_dsi_pixel_format_to_bpp); - -/** - * mipi_dsi_dcs_nop() - send DCS nop packet - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_nop); - -/** - * mipi_dsi_dcs_soft_reset() - perform a software reset of the display module - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset); - -/** - * mipi_dsi_dcs_get_power_mode() - query the display module's current power - * mode - * @dsi: DSI peripheral device - * @mode: return location for the current power mode - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode) -{ - ssize_t err; - - err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode, - sizeof(*mode)); - if (err <= 0) { - if (err == 0) - err = -ENODATA; - - return err; - } - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode); - -/** - * mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image - * data used by the interface - * @dsi: DSI peripheral device - * @format: return location for the pixel format - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format) -{ - ssize_t err; - - err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format, - sizeof(*format)); - if (err <= 0) { - if (err == 0) - err = -ENODATA; - - return err; - } - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format); - -/** - * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the - * display module except interface communication - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode); - -/** - * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display - * module - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode); - -/** - * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the - * display device - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off); - -/** - * mipi_dsi_dcs_set_display_on() - start displaying the image data on the - * display device - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure - */ -int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on); - -/** - * mipi_dsi_dcs_set_column_address() - define the column extent of the frame - * memory accessed by the host processor - * @dsi: DSI peripheral device - * @start: first column of frame memory - * @end: last column of frame memory - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, - u16 end) -{ - u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload, - sizeof(payload)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address); - -/** - * mipi_dsi_dcs_set_page_address() - define the page extent of the frame - * memory accessed by the host processor - * @dsi: DSI peripheral device - * @start: first page of frame memory - * @end: last page of frame memory - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, - u16 end) -{ - u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload, - sizeof(payload)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address); - -/** - * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect - * output signal on the TE signal line - * @dsi: DSI peripheral device - * - * Return: 0 on success or a negative error code on failure - */ -int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off); - -/** - * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect - * output signal on the TE signal line. - * @dsi: DSI peripheral device - * @mode: the Tearing Effect Output Line mode - * - * Return: 0 on success or a negative error code on failure - */ -int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, - enum mipi_dsi_dcs_tear_mode mode) -{ - u8 value = mode; - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value, - sizeof(value)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on); - -/** - * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image - * data used by the interface - * @dsi: DSI peripheral device - * @format: pixel format - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format) -{ - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format, - sizeof(format)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format); - -/** - * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for - * the Tearing Effect output signal of the display module - * @dsi: DSI peripheral device - * @scanline: scanline to use as trigger - * - * Return: 0 on success or a negative error code on failure - */ -int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline) -{ - u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8, - scanline & 0xff }; - ssize_t err; - - err = mipi_dsi_generic_write(dsi, payload, sizeof(payload)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline); - -/** - * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the - * display - * @dsi: DSI peripheral device - * @brightness: brightness value - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, - u16 brightness) -{ - u8 payload[2] = { brightness & 0xff, brightness >> 8 }; - ssize_t err; - - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, - payload, sizeof(payload)); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness); - -/** - * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value - * of the display - * @dsi: DSI peripheral device - * @brightness: brightness value - * - * Return: 0 on success or a negative error code on failure. - */ -int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, - u16 *brightness) -{ - ssize_t err; - - err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, - brightness, sizeof(*brightness)); - if (err <= 0) { - if (err == 0) - err = -ENODATA; - - return err; - } - - return 0; -} -EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness); - -MODULE_AUTHOR("Andrzej Hajda "); -MODULE_AUTHOR("Yannick Fertre "); -MODULE_DESCRIPTION("MIPI DSI Bus"); -MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/video/mipi_dsi.c b/drivers/video/mipi_dsi.c new file mode 100644 index 0000000..cdc3ef5 --- /dev/null +++ b/drivers/video/mipi_dsi.c @@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MIPI DSI Bus + * + * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved + * Andrzej Hajda + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Mipi_dsi.c contains a set of dsi helpers. + * This file is inspired from the drm helper file drivers/gpu/drm/drm_mipi_dsi.c + * (kernel linux). + * + */ + +#include +#include +#include +#include +#include +#include + +/** + * DOC: dsi helpers + * + * These functions contain some common logic and helpers to deal with MIPI DSI + * peripherals. + * + * Helpers are provided for a number of standard MIPI DSI command as well as a + * subset of the MIPI DCS command set. + */ + +/** + * mipi_dsi_attach - attach a DSI device to its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_attach(struct mipi_dsi_device *dsi) +{ + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (!ops || !ops->attach) + return -ENOSYS; + + return ops->attach(dsi->host, dsi); +} +EXPORT_SYMBOL(mipi_dsi_attach); + +/** + * mipi_dsi_detach - detach a DSI device from its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_detach(struct mipi_dsi_device *dsi) +{ + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (!ops || !ops->detach) + return -ENOSYS; + + return ops->detach(dsi->host, dsi); +} +EXPORT_SYMBOL(mipi_dsi_detach); + +/** + * mipi_dsi_device_transfer - transfer message to a DSI device + * @dsi: DSI peripheral + * @msg: message + */ +static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi, + struct mipi_dsi_msg *msg) +{ + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (!ops || !ops->transfer) + return -ENOSYS; + + if (dsi->mode_flags & MIPI_DSI_MODE_LPM) + msg->flags |= MIPI_DSI_MSG_USE_LPM; + + return ops->transfer(dsi->host, msg); +} + +/** + * mipi_dsi_packet_format_is_short - check if a packet is of the short format + * @type: MIPI DSI data type of the packet + * + * Return: true if the packet for the given data type is a short packet, false + * otherwise. + */ +bool mipi_dsi_packet_format_is_short(u8 type) +{ + switch (type) { + case MIPI_DSI_V_SYNC_START: + case MIPI_DSI_V_SYNC_END: + case MIPI_DSI_H_SYNC_START: + case MIPI_DSI_H_SYNC_END: + case MIPI_DSI_END_OF_TRANSMISSION: + case MIPI_DSI_COLOR_MODE_OFF: + case MIPI_DSI_COLOR_MODE_ON: + case MIPI_DSI_SHUTDOWN_PERIPHERAL: + case MIPI_DSI_TURN_ON_PERIPHERAL: + case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: + case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: + case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: + case MIPI_DSI_DCS_SHORT_WRITE: + case MIPI_DSI_DCS_SHORT_WRITE_PARAM: + case MIPI_DSI_DCS_READ: + case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: + return true; + } + + return false; +} +EXPORT_SYMBOL(mipi_dsi_packet_format_is_short); + +/** + * mipi_dsi_packet_format_is_long - check if a packet is of the long format + * @type: MIPI DSI data type of the packet + * + * Return: true if the packet for the given data type is a long packet, false + * otherwise. + */ +bool mipi_dsi_packet_format_is_long(u8 type) +{ + switch (type) { + case MIPI_DSI_NULL_PACKET: + case MIPI_DSI_BLANKING_PACKET: + case MIPI_DSI_GENERIC_LONG_WRITE: + case MIPI_DSI_DCS_LONG_WRITE: + case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20: + case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24: + case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16: + case MIPI_DSI_PACKED_PIXEL_STREAM_30: + case MIPI_DSI_PACKED_PIXEL_STREAM_36: + case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12: + case MIPI_DSI_PACKED_PIXEL_STREAM_16: + case MIPI_DSI_PACKED_PIXEL_STREAM_18: + case MIPI_DSI_PIXEL_STREAM_3BYTE_18: + case MIPI_DSI_PACKED_PIXEL_STREAM_24: + return true; + } + + return false; +} +EXPORT_SYMBOL(mipi_dsi_packet_format_is_long); + +/** + * mipi_dsi_create_packet - create a packet from a message according to the + * DSI protocol + * @packet: pointer to a DSI packet structure + * @msg: message to translate into a packet + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_create_packet(struct mipi_dsi_packet *packet, + const struct mipi_dsi_msg *msg) +{ + if (!packet || !msg) + return -EINVAL; + + /* do some minimum sanity checking */ + if (!mipi_dsi_packet_format_is_short(msg->type) && + !mipi_dsi_packet_format_is_long(msg->type)) + return -EINVAL; + + if (msg->channel > 3) + return -EINVAL; + + memset(packet, 0, sizeof(*packet)); + packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f); + + /* TODO: compute ECC if hardware support is not available */ + + /* + * Long write packets contain the word count in header bytes 1 and 2. + * The payload follows the header and is word count bytes long. + * + * Short write packets encode up to two parameters in header bytes 1 + * and 2. + */ + if (mipi_dsi_packet_format_is_long(msg->type)) { + packet->header[1] = (msg->tx_len >> 0) & 0xff; + packet->header[2] = (msg->tx_len >> 8) & 0xff; + + packet->payload_length = msg->tx_len; + packet->payload = msg->tx_buf; + } else { + const u8 *tx = msg->tx_buf; + + packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0; + packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0; + } + + packet->size = sizeof(packet->header) + packet->payload_length; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_create_packet); + +/** + * mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_SHUTDOWN_PERIPHERAL, + .tx_buf = (u8 [2]) { 0, 0 }, + .tx_len = 2, + }; + int ret = mipi_dsi_device_transfer(dsi, &msg); + + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral); + +/** + * mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_TURN_ON_PERIPHERAL, + .tx_buf = (u8 [2]) { 0, 0 }, + .tx_len = 2, + }; + int ret = mipi_dsi_device_transfer(dsi, &msg); + + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral); + +/* + * mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the + * the payload in a long packet transmitted from the peripheral back to the + * host processor + * @dsi: DSI peripheral device + * @value: the maximum size of the payload + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi, + u16 value) +{ + u8 tx[2] = { value & 0xff, value >> 8 }; + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, + .tx_len = sizeof(tx), + .tx_buf = tx, + }; + int ret = mipi_dsi_device_transfer(dsi, &msg); + + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size); + +/** + * mipi_dsi_generic_write() - transmit data using a generic write packet + * @dsi: DSI peripheral device + * @payload: buffer containing the payload + * @size: size of payload buffer + * + * This function will automatically choose the right data type depending on + * the payload length. + * + * Return: The number of bytes transmitted on success or a negative error code + * on failure. + */ +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload, + size_t size) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .tx_buf = payload, + .tx_len = size + }; + + switch (size) { + case 0: + msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM; + break; + + case 1: + msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM; + break; + + case 2: + msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM; + break; + + default: + msg.type = MIPI_DSI_GENERIC_LONG_WRITE; + break; + } + + return mipi_dsi_device_transfer(dsi, &msg); +} +EXPORT_SYMBOL(mipi_dsi_generic_write); + +/** + * mipi_dsi_generic_read() - receive data using a generic read packet + * @dsi: DSI peripheral device + * @params: buffer containing the request parameters + * @num_params: number of request parameters + * @data: buffer in which to return the received data + * @size: size of receive buffer + * + * This function will automatically choose the right data type depending on + * the number of parameters passed in. + * + * Return: The number of bytes successfully read or a negative error code on + * failure. + */ +ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params, + size_t num_params, void *data, size_t size) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .tx_len = num_params, + .tx_buf = params, + .rx_len = size, + .rx_buf = data + }; + + switch (num_params) { + case 0: + msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM; + break; + + case 1: + msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM; + break; + + case 2: + msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM; + break; + + default: + return -EINVAL; + } + + return mipi_dsi_device_transfer(dsi, &msg); +} +EXPORT_SYMBOL(mipi_dsi_generic_read); + +/** + * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload + * @dsi: DSI peripheral device + * @data: buffer containing data to be transmitted + * @len: size of transmission buffer + * + * This function will automatically choose the right data type depending on + * the command payload length. + * + * Return: The number of bytes successfully transmitted or a negative error + * code on failure. + */ +ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, + const void *data, size_t len) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .tx_buf = data, + .tx_len = len + }; + + switch (len) { + case 0: + return -EINVAL; + + case 1: + msg.type = MIPI_DSI_DCS_SHORT_WRITE; + break; + + case 2: + msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; + break; + + default: + msg.type = MIPI_DSI_DCS_LONG_WRITE; + break; + } + + return mipi_dsi_device_transfer(dsi, &msg); +} +EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer); + +/** + * mipi_dsi_dcs_write() - send DCS write command + * @dsi: DSI peripheral device + * @cmd: DCS command + * @data: buffer containing the command payload + * @len: command payload length + * + * This function will automatically choose the right data type depending on + * the command payload length. + * + * Return: The number of bytes successfully transmitted or a negative error + * code on failure. + */ +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, + const void *data, size_t len) +{ + ssize_t err; + size_t size; + u8 *tx; + + if (len > 0) { + size = 1 + len; + + tx = kmalloc(size, GFP_KERNEL); + if (!tx) + return -ENOMEM; + + /* concatenate the DCS command byte and the payload */ + tx[0] = cmd; + memcpy(&tx[1], data, len); + } else { + tx = &cmd; + size = 1; + } + + err = mipi_dsi_dcs_write_buffer(dsi, tx, size); + + if (len > 0) + kfree(tx); + + return err; +} +EXPORT_SYMBOL(mipi_dsi_dcs_write); + +/** + * mipi_dsi_dcs_read() - send DCS read request command + * @dsi: DSI peripheral device + * @cmd: DCS command + * @data: buffer in which to receive data + * @len: size of receive buffer + * + * Return: The number of bytes read or a negative error code on failure. + */ +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_DCS_READ, + .tx_buf = &cmd, + .tx_len = 1, + .rx_buf = data, + .rx_len = len + }; + + return mipi_dsi_device_transfer(dsi, &msg); +} +EXPORT_SYMBOL(mipi_dsi_dcs_read); + +/** + * mipi_dsi_dcs_nop() - send DCS nop packet + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_nop); + +/** + * mipi_dsi_dcs_soft_reset() - perform a software reset of the display module + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset); + +/** + * mipi_dsi_dcs_get_power_mode() - query the display module's current power + * mode + * @dsi: DSI peripheral device + * @mode: return location for the current power mode + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode) +{ + ssize_t err; + + err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode, + sizeof(*mode)); + if (err <= 0) { + if (err == 0) + err = -ENODATA; + + return err; + } + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode); + +/** + * mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: return location for the pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format) +{ + ssize_t err; + + err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format, + sizeof(*format)); + if (err <= 0) { + if (err == 0) + err = -ENODATA; + + return err; + } + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format); + +/** + * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the + * display module except interface communication + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode); + +/** + * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display + * module + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode); + +/** + * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the + * display device + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off); + +/** + * mipi_dsi_dcs_set_display_on() - start displaying the image data on the + * display device + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on); + +/** + * mipi_dsi_dcs_set_column_address() - define the column extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first column of frame memory + * @end: last column of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, + u16 end) +{ + u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload, + sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address); + +/** + * mipi_dsi_dcs_set_page_address() - define the page extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first page of frame memory + * @end: last page of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, + u16 end) +{ + u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload, + sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address); + +/** + * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect + * output signal on the TE signal line + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off); + +/** + * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect + * output signal on the TE signal line. + * @dsi: DSI peripheral device + * @mode: the Tearing Effect Output Line mode + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, + enum mipi_dsi_dcs_tear_mode mode) +{ + u8 value = mode; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value, + sizeof(value)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on); + +/** + * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format, + sizeof(format)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format); + +/** + * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for + * the Tearing Effect output signal of the display module + * @dsi: DSI peripheral device + * @scanline: scanline to use as trigger + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline) +{ + u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8, + scanline & 0xff }; + ssize_t err; + + err = mipi_dsi_generic_write(dsi, payload, sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline); + +/** + * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the + * display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, + u16 brightness) +{ + u8 payload[2] = { brightness & 0xff, brightness >> 8 }; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + payload, sizeof(payload)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness); + +/** + * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value + * of the display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, + u16 *brightness) +{ + ssize_t err; + + err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, + brightness, sizeof(*brightness)); + if (err <= 0) { + if (err == 0) + err = -ENODATA; + + return err; + } + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness); diff --git a/drivers/video/orisetech_otm8009a.c b/drivers/video/orisetech_otm8009a.c index 41c4bef..629bf76 100644 --- a/drivers/video/orisetech_otm8009a.c +++ b/drivers/video/orisetech_otm8009a.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 STMicroelectronics - All Rights Reserved + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved * Author(s): Yannick Fertre for STMicroelectronics. * Philippe Cornu for STMicroelectronics. * - * This otm8009a panel driver is based on the Linux Kernel driver from + * This otm8009a panel driver is inspired from the Linux Kernel driver * drivers/gpu/drm/panel/panel-orisetech-otm8009a.c. */ #include #include #include -#include +#include #include #include #include @@ -63,15 +63,15 @@ struct otm8009a_panel_priv { }; static const struct display_timing default_timing = { - .pixelclock = {.min = 32729000, .typ = 32729000, .max = 32729000,}, - .hactive = {.min = 480, .typ = 480, .max = 480,}, - .hfront_porch = {.min = 120, .typ = 120, .max = 120,}, - .hback_porch = {.min = 120, .typ = 120, .max = 120,}, - .hsync_len = {.min = 63, .typ = 63, .max = 63,}, - .vactive = {.min = 800, .typ = 800, .max = 800,}, - .vfront_porch = {.min = 12, .typ = 12, .max = 12,}, - .vback_porch = {.min = 12, .typ = 12, .max = 12,}, - .vsync_len = {.min = 12, .typ = 12, .max = 12,}, + .pixelclock.typ = 29700000, + .hactive.typ = 480, + .hfront_porch.typ = 98, + .hback_porch.typ = 98, + .hsync_len.typ = 32, + .vactive.typ = 800, + .vfront_porch.typ = 15, + .vback_porch.typ = 14, + .vsync_len.typ = 10, }; static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data, @@ -84,12 +84,34 @@ static void otm8009a_dcs_write_buf(struct udevice *dev, const void *data, dev_err(dev, "mipi dsi dcs write buffer failed\n"); } +static void otm8009a_dcs_write_buf_hs(struct udevice *dev, const void *data, + size_t len) +{ + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); + struct mipi_dsi_device *device = plat->device; + + /* data will be sent in dsi hs mode (ie. no lpm) */ + device->mode_flags &= ~MIPI_DSI_MODE_LPM; + + if (mipi_dsi_dcs_write_buffer(device, data, len) < 0) + dev_err(dev, "mipi dsi dcs write buffer failed\n"); + + /* restore back the dsi lpm mode */ + device->mode_flags |= MIPI_DSI_MODE_LPM; +} + #define dcs_write_seq(dev, seq...) \ ({ \ static const u8 d[] = { seq }; \ otm8009a_dcs_write_buf(dev, d, ARRAY_SIZE(d)); \ }) +#define dcs_write_seq_hs(dev, seq...) \ +({ \ + static const u8 d[] = { seq }; \ + otm8009a_dcs_write_buf_hs(dev, d, ARRAY_SIZE(d)); \ +}) + #define dcs_write_cmd_at(dev, cmd, seq...) \ ({ \ static const u16 c = cmd; \ @@ -196,11 +218,13 @@ static int otm8009a_init_sequence(struct udevice *dev) /* Default portrait 480x800 rgb24 */ dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00); - ret = mipi_dsi_dcs_set_column_address(device, 0, 479); + ret = mipi_dsi_dcs_set_column_address(device, 0, + default_timing.hactive.typ - 1); if (ret) return ret; - ret = mipi_dsi_dcs_set_page_address(device, 0, 799); + ret = mipi_dsi_dcs_set_page_address(device, 0, + default_timing.vactive.typ - 1); if (ret) return ret; @@ -213,6 +237,17 @@ static int otm8009a_init_sequence(struct udevice *dev) /* Disable CABC feature */ dcs_write_seq(dev, MIPI_DCS_WRITE_POWER_SAVE, 0x00); + ret = mipi_dsi_dcs_set_display_on(device); + if (ret) + return ret; + + ret = mipi_dsi_dcs_nop(device); + if (ret) + return ret; + + /* Send Command GRAM memory write (no parameters) */ + dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START); + return 0; } @@ -222,12 +257,6 @@ static int otm8009a_panel_enable_backlight(struct udevice *dev) struct mipi_dsi_device *device = plat->device; int ret; - device->lanes = 2; - device->format = MIPI_DSI_FMT_RGB888; - device->mode_flags = MIPI_DSI_MODE_VIDEO | - MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_LPM; - ret = mipi_dsi_attach(device); if (ret < 0) return ret; @@ -247,18 +276,10 @@ static int otm8009a_panel_enable_backlight(struct udevice *dev) /* Update Brightness Control & Backlight */ dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); - ret = mipi_dsi_dcs_set_display_on(device); - if (ret) - return ret; - - ret = mipi_dsi_dcs_nop(device); - if (ret) - return ret; - - /* Send Command GRAM memory write (no parameters) */ - dcs_write_seq(dev, MIPI_DCS_WRITE_MEMORY_START); + /* Update Brightness Control & Backlight */ + dcs_write_seq_hs(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY); - /* need to wait a few time before set the DSI bridge in video mode */ + /* Need to wait a few time before sending the first image */ mdelay(10); return 0; @@ -268,6 +289,7 @@ static int otm8009a_panel_get_display_timing(struct udevice *dev, struct display_timing *timings) { memcpy(timings, &default_timing, sizeof(*timings)); + return 0; } @@ -299,23 +321,28 @@ static int otm8009a_panel_ofdata_to_platdata(struct udevice *dev) static int otm8009a_panel_probe(struct udevice *dev) { struct otm8009a_panel_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); int ret; if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) { dev_dbg(dev, "enable regulator '%s'\n", priv->reg->name); ret = regulator_set_enable(priv->reg, true); - if (ret) { - dev_err(dev, "Regulator : '%s' - can't enable\n", - priv->reg->name); + if (ret) return ret; - } } /* reset panel */ dm_gpio_set_value(&priv->reset, true); - mdelay(1); + mdelay(1); /* >50us */ dm_gpio_set_value(&priv->reset, false); - mdelay(10); + mdelay(10); /* >5ms */ + + /* fill characteristics of DSI data link */ + plat->lanes = 2; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM; return 0; } diff --git a/drivers/video/raydium-rm68200.c b/drivers/video/raydium-rm68200.c index 3347f12..76b0ed2 100644 --- a/drivers/video/raydium-rm68200.c +++ b/drivers/video/raydium-rm68200.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 STMicroelectronics - All Rights Reserved + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved * Author(s): Yannick Fertre for STMicroelectronics. * Philippe Cornu for STMicroelectronics. * - * This rm68200 panel driver is based on the Linux Kernel driver from + * This rm68200 panel driver is inspired from the Linux Kernel driver * drivers/gpu/drm/panel/panel-raydium-rm68200.c. */ #include #include #include -#include +#include #include #include #include @@ -76,15 +76,15 @@ struct rm68200_panel_priv { }; static const struct display_timing default_timing = { - .pixelclock = {.min = 52582000, .typ = 52582000, .max = 52582000,}, - .hactive = {.min = 720, .typ = 720, .max = 720,}, - .hfront_porch = {.min = 38, .typ = 38, .max = 38,}, - .hback_porch = {.min = 8, .typ = 8, .max = 8,}, - .hsync_len = {.min = 38, .typ = 38, .max = 38,}, - .vactive = {.min = 1280, .typ = 1280, .max = 1280,}, - .vfront_porch = {.min = 12, .typ = 12, .max = 12,}, - .vback_porch = {.min = 4, .typ = 4, .max = 4,}, - .vsync_len = {.min = 12, .typ = 12, .max = 12,}, + .pixelclock.typ = 54000000, + .hactive.typ = 720, + .hfront_porch.typ = 48, + .hback_porch.typ = 48, + .hsync_len.typ = 9, + .vactive.typ = 1280, + .vfront_porch.typ = 12, + .vback_porch.typ = 12, + .vsync_len.typ = 5, }; static void rm68200_dcs_write_buf(struct udevice *dev, const void *data, @@ -226,12 +226,6 @@ static int rm68200_panel_enable_backlight(struct udevice *dev) struct rm68200_panel_priv *priv = dev_get_priv(dev); int ret; - device->lanes = 2; - device->format = MIPI_DSI_FMT_RGB888; - device->mode_flags = MIPI_DSI_MODE_VIDEO | - MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_LPM; - ret = mipi_dsi_attach(device); if (ret < 0) return ret; @@ -261,6 +255,7 @@ static int rm68200_panel_get_display_timing(struct udevice *dev, struct display_timing *timings) { memcpy(timings, &default_timing, sizeof(*timings)); + return 0; } @@ -299,6 +294,7 @@ static int rm68200_panel_ofdata_to_platdata(struct udevice *dev) static int rm68200_panel_probe(struct udevice *dev) { struct rm68200_panel_priv *priv = dev_get_priv(dev); + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); int ret; if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) { @@ -313,6 +309,13 @@ static int rm68200_panel_probe(struct udevice *dev) dm_gpio_set_value(&priv->reset, false); mdelay(10); + /* fill characteristics of DSI data link */ + plat->lanes = 2; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM; + return 0; } diff --git a/drivers/video/stm32/stm32_dsi.c b/drivers/video/stm32/stm32_dsi.c index f8f7c83..022ae96 100644 --- a/drivers/video/stm32/stm32_dsi.c +++ b/drivers/video/stm32/stm32_dsi.c @@ -1,17 +1,18 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 STMicroelectronics - All Rights Reserved + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved * Author(s): Philippe Cornu for STMicroelectronics. * Yannick Fertre for STMicroelectronics. * * This MIPI DSI controller driver is based on the Linux Kernel driver from * drivers/gpu/drm/stm/dw_mipi_dsi-stm.c. */ + #include #include #include -#include -#include +#include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -87,6 +89,7 @@ struct stm32_dsi_priv { int lane_min_kbps; int lane_max_kbps; struct udevice *vdd_reg; + struct udevice *dsi_host; }; static inline void dsi_write(struct stm32_dsi_priv *dsi, u32 reg, u32 val) @@ -207,12 +210,14 @@ static int dsi_phy_init(void *priv_data) u32 val; int ret; + debug("Initialize DSI physical layer\n"); + /* Enable the regulator */ dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, TIMEOUT_US); if (ret) { - dev_err(dev, "!TIMEOUT! waiting REGU\n"); + debug("!TIMEOUT! waiting REGU\n"); return ret; } @@ -221,16 +226,37 @@ static int dsi_phy_init(void *priv_data) ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, TIMEOUT_US); if (ret) { - dev_err(dev, "!TIMEOUT! waiting PLL\n"); + debug("!TIMEOUT! waiting PLL\n"); return ret; } - /* Enable the DSI wrapper */ - dsi_set(dsi, DSI_WCR, WCR_DSIEN); - return 0; } +static void dsi_phy_post_set_mode(void *priv_data, unsigned long mode_flags) +{ + struct mipi_dsi_device *device = priv_data; + struct udevice *dev = device->dev; + struct stm32_dsi_priv *dsi = dev_get_priv(dev); + + debug("Set mode %p enable %ld\n", dsi, + mode_flags & MIPI_DSI_MODE_VIDEO); + + if (!dsi) + return; + + /* + * DSI wrapper must be enabled in video mode & disabled in command mode. + * If wrapper is enabled in command mode, the display controller + * register access will hang. + */ + + if (mode_flags & MIPI_DSI_MODE_VIDEO) + dsi_set(dsi, DSI_WCR, WCR_DSIEN); + else + dsi_clear(dsi, DSI_WCR, WCR_DSIEN); +} + static int dsi_get_lane_mbps(void *priv_data, struct display_timing *timings, u32 lanes, u32 format, unsigned int *lane_mbps) { @@ -302,28 +328,51 @@ static int dsi_get_lane_mbps(void *priv_data, struct display_timing *timings, return 0; } -static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { +static const struct mipi_dsi_phy_ops dsi_stm_phy_ops = { .init = dsi_phy_init, .get_lane_mbps = dsi_get_lane_mbps, + .post_set_mode = dsi_phy_post_set_mode, }; static int stm32_dsi_attach(struct udevice *dev) { - struct stm32_dsi_priv *dsi = dev_get_priv(dev); - struct dw_mipi_dsi_plat_data *platdata = dev_get_platdata(dev); - struct mipi_dsi_device *device = &dsi->device; + struct stm32_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mplat; + struct display_timing timings; int ret; - platdata->max_data_lanes = 2; - platdata->phy_ops = &dw_mipi_dsi_stm_phy_ops; - - ret = uclass_first_device(UCLASS_PANEL, &platdata->panel); + ret = uclass_first_device(UCLASS_PANEL, &priv->panel); if (ret) { dev_err(dev, "panel device error %d\n", ret); return ret; } - ret = dw_mipi_dsi_init_bridge(device); + mplat = dev_get_platdata(priv->panel); + mplat->device = &priv->device; + device->lanes = mplat->lanes; + device->format = mplat->format; + device->mode_flags = mplat->mode_flags; + + ret = panel_get_display_timing(priv->panel, &timings); + if (ret) { + ret = fdtdec_decode_display_timing(gd->fdt_blob, + dev_of_offset(priv->panel), + 0, &timings); + if (ret) { + dev_err(dev, "decode display timing error %d\n", ret); + return ret; + } + } + + ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host); + if (ret) { + dev_err(dev, "No video dsi host detected %d\n", ret); + return ret; + } + + ret = dsi_host_init(priv->dsi_host, device, &timings, 2, + &dsi_stm_phy_ops); if (ret) { dev_err(dev, "failed to initialize mipi dsi host\n"); return ret; @@ -334,103 +383,116 @@ static int stm32_dsi_attach(struct udevice *dev) static int stm32_dsi_set_backlight(struct udevice *dev, int percent) { - struct dw_mipi_dsi_plat_data *dplat = dev_get_platdata(dev); - struct stm32_dsi_priv *dsi = dev_get_priv(dev); - struct mipi_dsi_device *device = &dsi->device; - struct udevice *panel = dplat->panel; - struct mipi_dsi_panel_plat *mplat; + struct stm32_dsi_priv *priv = dev_get_priv(dev); int ret; - mplat = dev_get_platdata(panel); - mplat->device = device; - - ret = panel_enable_backlight(panel); + ret = panel_enable_backlight(priv->panel); if (ret) { dev_err(dev, "panel %s enable backlight error %d\n", - panel->name, ret); + priv->panel->name, ret); return ret; } - dw_mipi_dsi_bridge_enable(device); + ret = dsi_host_enable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } return 0; } +static int stm32_dsi_bind(struct udevice *dev) +{ + int ret; + + ret = device_bind_driver_to_node(dev, "dw_mipi_dsi", "dsihost", + dev_ofnode(dev), NULL); + if (ret) + return ret; + + return dm_scan_fdt_dev(dev); +} + static int stm32_dsi_probe(struct udevice *dev) { - struct stm32_dsi_priv *dsi = dev_get_priv(dev); - struct mipi_dsi_device *device = &dsi->device; + struct stm32_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; struct reset_ctl rst; struct clk clk; int ret; device->dev = dev; - dsi->base = (void *)dev_read_addr(dev); - if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) { + priv->base = (void *)dev_read_addr(dev); + if ((fdt_addr_t)priv->base == FDT_ADDR_T_NONE) { dev_err(dev, "dsi dt register address error\n"); return -EINVAL; } if (IS_ENABLED(CONFIG_DM_REGULATOR)) { ret = device_get_supply_regulator(dev, "phy-dsi-supply", - &dsi->vdd_reg); + &priv->vdd_reg); if (ret && ret != -ENOENT) { dev_err(dev, "Warning: cannot get phy dsi supply\n"); return -ENODEV; } - ret = regulator_set_enable(dsi->vdd_reg, true); - if (ret) - return -ENODEV; + if (ret != -ENOENT) { + ret = regulator_set_enable(priv->vdd_reg, true); + if (ret) + return ret; + } } ret = clk_get_by_name(device->dev, "pclk", &clk); if (ret) { dev_err(dev, "peripheral clock get error %d\n", ret); - regulator_set_enable(dsi->vdd_reg, false); - return -ENODEV; + goto err_reg; } ret = clk_enable(&clk); if (ret) { dev_err(dev, "peripheral clock enable error %d\n", ret); - regulator_set_enable(dsi->vdd_reg, false); - return -ENODEV; + goto err_reg; } ret = clk_get_by_name(dev, "ref", &clk); if (ret) { dev_err(dev, "pll reference clock get error %d\n", ret); - clk_disable(&clk); - regulator_set_enable(dsi->vdd_reg, false); - return ret; + goto err_clk; } - dsi->pllref_clk = (unsigned int)clk_get_rate(&clk); + priv->pllref_clk = (unsigned int)clk_get_rate(&clk); ret = reset_get_by_index(device->dev, 0, &rst); if (ret) { dev_err(dev, "missing dsi hardware reset\n"); - clk_disable(&clk); - regulator_set_enable(dsi->vdd_reg, false); - return -ENODEV; + goto err_clk; } /* Reset */ reset_deassert(&rst); /* check hardware version */ - dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; - if (dsi->hw_version != HWVER_130 && - dsi->hw_version != HWVER_131) { + priv->hw_version = dsi_read(priv, DSI_VERSION) & VERSION; + if (priv->hw_version != HWVER_130 && + priv->hw_version != HWVER_131) { dev_err(dev, "bad dsi hardware version\n"); clk_disable(&clk); - regulator_set_enable(dsi->vdd_reg, false); + if (IS_ENABLED(CONFIG_DM_REGULATOR)) + regulator_set_enable(priv->vdd_reg, false); return -ENODEV; } return 0; +err_clk: + clk_disable(&clk); +err_reg: + if (IS_ENABLED(CONFIG_DM_REGULATOR)) + regulator_set_enable(priv->vdd_reg, false); + + return ret; } struct video_bridge_ops stm32_dsi_ops = { @@ -447,9 +509,8 @@ U_BOOT_DRIVER(stm32_dsi) = { .name = "stm32-display-dsi", .id = UCLASS_VIDEO_BRIDGE, .of_match = stm32_dsi_ids, - .bind = dm_scan_fdt_dev, + .bind = stm32_dsi_bind, .probe = stm32_dsi_probe, .ops = &stm32_dsi_ops, .priv_auto_alloc_size = sizeof(struct stm32_dsi_priv), - .platdata_auto_alloc_size = sizeof(struct dw_mipi_dsi_plat_data), }; diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index 8c996b8..59ff692 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -4,6 +4,7 @@ * Author(s): Philippe Cornu for STMicroelectronics. * Yannick Fertre for STMicroelectronics. */ + #include #include #include @@ -328,9 +329,7 @@ static int stm32_ltdc_probe(struct udevice *dev) struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct stm32_ltdc_priv *priv = dev_get_priv(dev); -#ifdef CONFIG_VIDEO_BRIDGE struct udevice *bridge = NULL; -#endif struct udevice *panel = NULL; struct display_timing timings; struct clk pclk; @@ -390,19 +389,20 @@ static int stm32_ltdc_probe(struct udevice *dev) /* Reset */ reset_deassert(&rst); -#ifdef CONFIG_VIDEO_BRIDGE - ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge); - if (ret) - debug("No video bridge, or no backlight on bridge\n"); - - if (bridge) { - ret = video_bridge_attach(bridge); - if (ret) { - dev_err(dev, "fail to attach bridge\n"); - return ret; + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge); + if (ret) + debug("No video bridge, or no backlight on bridge\n"); + + if (bridge) { + ret = video_bridge_attach(bridge); + if (ret) { + dev_err(dev, "fail to attach bridge\n"); + return ret; + } } } -#endif + /* TODO Below parameters are hard-coded for the moment... */ priv->l2bpp = VIDEO_BPP16; priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */ @@ -428,29 +428,21 @@ static int stm32_ltdc_probe(struct udevice *dev) uc_priv->ysize = timings.vactive.typ; uc_priv->bpix = priv->l2bpp; -#ifdef CONFIG_VIDEO_BRIDGE - if (bridge) { - ret = video_bridge_set_backlight(bridge, 80); - if (ret) { - dev_err(dev, "fail to set backlight\n"); - return ret; - } - } else { + if (!bridge) { ret = panel_enable_backlight(panel); if (ret) { dev_err(dev, "panel %s enable backlight error %d\n", panel->name, ret); return ret; } + } else if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + ret = video_bridge_set_backlight(bridge, 80); + if (ret) { + dev_err(dev, "fail to set backlight\n"); + return ret; + } } -#else - ret = panel_enable_backlight(panel); - if (ret) { - dev_err(dev, "panel %s enable backlight error %d\n", - panel->name, ret); - return ret; - } -#endif + video_set_flush_dcache(dev, true); return 0; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 6193017..5f62167 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -35,6 +35,7 @@ enum uclass_id { UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ + UCLASS_DSI_HOST, /* Dsi host device */ UCLASS_DMA, /* Direct Memory Access */ UCLASS_EFI, /* EFI managed devices */ UCLASS_ETH, /* Ethernet device */ diff --git a/include/dsi_host.h b/include/dsi_host.h new file mode 100644 index 0000000..9ff226f --- /dev/null +++ b/include/dsi_host.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved + * Author(s): Yannick Fertre for STMicroelectronics. + * + */ + +#ifndef _DSI_HOST_H +#define _DSI_HOST_H + +#include + +struct dsi_host_ops { + /** + * init() - Enable the dsi_host + * + * @dev: dsi host device + * @return 0 if OK, -ve on error + */ + int (*init)(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops); + + /** + * enable() - Enable the dsi_host + * + * @dev: dsi host device + * @return 0 if OK, -ve on error + */ + int (*enable)(struct udevice *dev); +}; + +#define dsi_host_get_ops(dev) ((struct dsi_host_ops *)(dev)->driver->ops) + +/** + * dsi_host_init + * + * @dev: dsi host device + * @return 0 if OK, -ve on error + */ +int dsi_host_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops); + +/** + * dsi_host_enable + * + * @dev: dsi host device + * @return 0 if OK, -ve on error + */ +int dsi_host_enable(struct udevice *dev); + +#endif diff --git a/include/mipi_display.h b/include/mipi_display.h index 5c3dbbe..19aa65a 100644 --- a/include/mipi_display.h +++ b/include/mipi_display.h @@ -4,16 +4,12 @@ * * Copyright (C) 2010 Guennadi Liakhovetski * Copyright (C) 2006 Nokia Corporation - * Copyright (C) 2018 STMicroelectronics - All Rights Reserved - * Author(s): Imre Deak - * Yannick Fertre - * Philippe Cornu + * Author: Imre Deak * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #ifndef MIPI_DISPLAY_H #define MIPI_DISPLAY_H @@ -139,247 +135,4 @@ enum { #define MIPI_DCS_PIXEL_FMT_8BIT 2 #define MIPI_DCS_PIXEL_FMT_3BIT 1 -struct mipi_dsi_host; -struct mipi_dsi_device; - -/* request ACK from peripheral */ -#define MIPI_DSI_MSG_REQ_ACK BIT(0) -/* use Low Power Mode to transmit message */ -#define MIPI_DSI_MSG_USE_LPM BIT(1) - -/** - * struct mipi_dsi_msg - read/write DSI buffer - * @channel: virtual channel id - * @type: payload data type - * @flags: flags controlling this message transmission - * @tx_len: length of @tx_buf - * @tx_buf: data to be written - * @rx_len: length of @rx_buf - * @rx_buf: data to be read, or NULL - */ -struct mipi_dsi_msg { - u8 channel; - u8 type; - u16 flags; - - size_t tx_len; - const void *tx_buf; - - size_t rx_len; - void *rx_buf; -}; - -bool mipi_dsi_packet_format_is_short(u8 type); -bool mipi_dsi_packet_format_is_long(u8 type); - -/** - * struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format - * @size: size (in bytes) of the packet - * @header: the four bytes that make up the header (Data ID, Word Count or - * Packet Data, and ECC) - * @payload_length: number of bytes in the payload - * @payload: a pointer to a buffer containing the payload, if any - */ -struct mipi_dsi_packet { - size_t size; - u8 header[4]; - size_t payload_length; - const u8 *payload; -}; - -int mipi_dsi_create_packet(struct mipi_dsi_packet *packet, - const struct mipi_dsi_msg *msg); - -/** - * struct mipi_dsi_host_ops - DSI bus operations - * @attach: attach DSI device to DSI host - * @detach: detach DSI device from DSI host - * @transfer: transmit a DSI packet - * - * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg - * structures. This structure contains information about the type of packet - * being transmitted as well as the transmit and receive buffers. When an - * error is encountered during transmission, this function will return a - * negative error code. On success it shall return the number of bytes - * transmitted for write packets or the number of bytes received for read - * packets. - * - * Note that typically DSI packet transmission is atomic, so the .transfer() - * function will seldomly return anything other than the number of bytes - * contained in the transmit buffer on success. - */ -struct mipi_dsi_host_ops { - int (*attach)(struct mipi_dsi_host *host, - struct mipi_dsi_device *dsi); - int (*detach)(struct mipi_dsi_host *host, - struct mipi_dsi_device *dsi); - ssize_t (*transfer)(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg); -}; - -/** - * struct mipi_dsi_host - DSI host device - * @dev: driver model device node for this DSI host - * @ops: DSI host operations - * @list: list management - */ -struct mipi_dsi_host { - struct device *dev; - const struct mipi_dsi_host_ops *ops; - struct list_head list; -}; - -/* DSI mode flags */ - -/* video mode */ -#define MIPI_DSI_MODE_VIDEO BIT(0) -/* video burst mode */ -#define MIPI_DSI_MODE_VIDEO_BURST BIT(1) -/* video pulse mode */ -#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2) -/* enable auto vertical count mode */ -#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3) -/* enable hsync-end packets in vsync-pulse and v-porch area */ -#define MIPI_DSI_MODE_VIDEO_HSE BIT(4) -/* disable hfront-porch area */ -#define MIPI_DSI_MODE_VIDEO_HFP BIT(5) -/* disable hback-porch area */ -#define MIPI_DSI_MODE_VIDEO_HBP BIT(6) -/* disable hsync-active area */ -#define MIPI_DSI_MODE_VIDEO_HSA BIT(7) -/* flush display FIFO on vsync pulse */ -#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) -/* disable EoT packets in HS mode */ -#define MIPI_DSI_MODE_EOT_PACKET BIT(9) -/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ -#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) -/* transmit data in low power */ -#define MIPI_DSI_MODE_LPM BIT(11) - -enum mipi_dsi_pixel_format { - MIPI_DSI_FMT_RGB888, - MIPI_DSI_FMT_RGB666, - MIPI_DSI_FMT_RGB666_PACKED, - MIPI_DSI_FMT_RGB565, -}; - -#define DSI_DEV_NAME_SIZE 20 - -/** - * struct mipi_dsi_device_info - template for creating a mipi_dsi_device - * @type: DSI peripheral chip type - * @channel: DSI virtual channel assigned to peripheral - * @node: pointer to OF device node or NULL - * - * This is populated and passed to mipi_dsi_device_new to create a new - * DSI device - */ -struct mipi_dsi_device_info { - char type[DSI_DEV_NAME_SIZE]; - u32 channel; - struct device_node *node; -}; - -/** - * struct mipi_dsi_device - DSI peripheral device - * @host: DSI host for this peripheral - * @dev: driver model device node for this peripheral - * @name: DSI peripheral chip type - * @channel: virtual channel assigned to the peripheral - * @format: pixel format for video mode - * @lanes: number of active data lanes - * @mode_flags: DSI operation mode related flags - */ -struct mipi_dsi_device { - struct mipi_dsi_host *host; - struct udevice *dev; - - char name[DSI_DEV_NAME_SIZE]; - unsigned int channel; - unsigned int lanes; - enum mipi_dsi_pixel_format format; - unsigned long mode_flags; -}; - -/** - * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode - * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking - * information only - * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both - * V-Blanking and H-Blanking information - */ -enum mipi_dsi_dcs_tear_mode { - MIPI_DSI_DCS_TEAR_MODE_VBLANK, - MIPI_DSI_DCS_TEAR_MODE_VHBLANK, -}; - -/** - * struct mipi_dsi_panel_plat - DSI panel platform data - * @device: DSI peripheral device - */ -struct mipi_dsi_panel_plat { - struct mipi_dsi_device *device; -}; - -int mipi_dsi_attach(struct mipi_dsi_device *dsi); -int mipi_dsi_detach(struct mipi_dsi_device *dsi); -int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt); - -/* bit compatible with the xrandr RR_ definitions (bits 0-13) - * - * ABI warning: Existing userspace really expects - * the mode flags to match the xrandr definitions. Any - * changes that don't match the xrandr definitions will - * likely need a new client cap or some other mechanism - * to avoid breaking existing userspace. This includes - * allocating new flags in the previously unused bits! - */ -#define MIPI_DSI_FLAG_PHSYNC BIT(0) -#define MIPI_DSI_FLAG_NHSYNC BIT(1) -#define MIPI_DSI_FLAG_PVSYNC BIT(2) -#define MIPI_DSI_FLAG_NVSYNC BIT(3) -#define MIPI_DSI_FLAG_INTERLACE BIT(4) -#define MIPI_DSI_FLAG_DBLSCAN BIT(5) -#define MIPI_DSI_FLAG_CSYNC BIT(6) -#define MIPI_DSI_FLAG_PCSYNC BIT(7) -#define MIPI_DSI_FLAG_NCSYNC BIT(8) -#define MIPI_DSI_FLAG_HSKEW BIT(9) /* hskew provided */ -#define MIPI_DSI_FLAG_BCAST BIT(10) -#define MIPI_DSI_FLAG_PIXMUX BIT(11) -#define MIPI_DSI_FLAG_DBLCLK BIT(12) -#define MIPI_DSI_FLAG_CLKDIV2 BIT(13) - -/* request ACK from peripheral */ -#define MIPI_DSI_MSG_REQ_ACK BIT(0) -/* use Low Power Mode to transmit message */ -#define MIPI_DSI_MSG_USE_LPM BIT(1) - -ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, - const void *data, size_t len); -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, - const void *data, size_t len); -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, - size_t len); -int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode); -int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format); -int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, - u16 end); -int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, - u16 end); -int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, - enum mipi_dsi_dcs_tear_mode mode); -int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); -int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline); -int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, - u16 brightness); -int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, - u16 *brightness); - #endif diff --git a/include/mipi_dsi.h b/include/mipi_dsi.h new file mode 100644 index 0000000..f76e8d7 --- /dev/null +++ b/include/mipi_dsi.h @@ -0,0 +1,473 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MIPI DSI Bus + * + * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Copyright (C) 2018 STMicroelectronics - All Rights Reserved + * Author(s): Andrzej Hajda + * Yannick Fertre + * Philippe Cornu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef MIPI_DSI_H +#define MIPI_DSI_H + +#include +#include + +struct mipi_dsi_host; +struct mipi_dsi_device; + +/* request ACK from peripheral */ +#define MIPI_DSI_MSG_REQ_ACK BIT(0) +/* use Low Power Mode to transmit message */ +#define MIPI_DSI_MSG_USE_LPM BIT(1) + +/** + * struct mipi_dsi_msg - read/write DSI buffer + * @channel: virtual channel id + * @type: payload data type + * @flags: flags controlling this message transmission + * @tx_len: length of @tx_buf + * @tx_buf: data to be written + * @rx_len: length of @rx_buf + * @rx_buf: data to be read, or NULL + */ +struct mipi_dsi_msg { + u8 channel; + u8 type; + u16 flags; + + size_t tx_len; + const void *tx_buf; + + size_t rx_len; + void *rx_buf; +}; + +bool mipi_dsi_packet_format_is_short(u8 type); +bool mipi_dsi_packet_format_is_long(u8 type); + +/** + * struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format + * @size: size (in bytes) of the packet + * @header: the four bytes that make up the header (Data ID, Word Count or + * Packet Data, and ECC) + * @payload_length: number of bytes in the payload + * @payload: a pointer to a buffer containing the payload, if any + */ +struct mipi_dsi_packet { + size_t size; + u8 header[4]; + size_t payload_length; + const u8 *payload; +}; + +int mipi_dsi_create_packet(struct mipi_dsi_packet *packet, + const struct mipi_dsi_msg *msg); + +/** + * struct mipi_dsi_host_ops - DSI bus operations + * @attach: attach DSI device to DSI host + * @detach: detach DSI device from DSI host + * @transfer: transmit a DSI packet + * + * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg + * structures. This structure contains information about the type of packet + * being transmitted as well as the transmit and receive buffers. When an + * error is encountered during transmission, this function will return a + * negative error code. On success it shall return the number of bytes + * transmitted for write packets or the number of bytes received for read + * packets. + * + * Note that typically DSI packet transmission is atomic, so the .transfer() + * function will seldomly return anything other than the number of bytes + * contained in the transmit buffer on success. + */ +struct mipi_dsi_host_ops { + int (*attach)(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi); + int (*detach)(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi); + ssize_t (*transfer)(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +}; + +/** + * struct mipi_dsi_phy_ops - DSI host physical operations + * @init: initialized host physical part + * @get_lane_mbps: get lane bitrate per lane (mbps) + * @post_set_mode: operation that should after set mode + */ +struct mipi_dsi_phy_ops { + int (*init)(void *priv_data); + int (*get_lane_mbps)(void *priv_data, struct display_timing *timings, + u32 lanes, u32 format, unsigned int *lane_mbps); + void (*post_set_mode)(void *priv_data, unsigned long mode_flags); +}; + +/** + * struct mipi_dsi_host - DSI host device + * @dev: driver model device node for this DSI host + * @ops: DSI host operations + * @list: list management + */ +struct mipi_dsi_host { + struct device *dev; + const struct mipi_dsi_host_ops *ops; + struct list_head list; +}; + +/* DSI mode flags */ + +/* video mode */ +#define MIPI_DSI_MODE_VIDEO BIT(0) +/* video burst mode */ +#define MIPI_DSI_MODE_VIDEO_BURST BIT(1) +/* video pulse mode */ +#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2) +/* enable auto vertical count mode */ +#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3) +/* enable hsync-end packets in vsync-pulse and v-porch area */ +#define MIPI_DSI_MODE_VIDEO_HSE BIT(4) +/* disable hfront-porch area */ +#define MIPI_DSI_MODE_VIDEO_HFP BIT(5) +/* disable hback-porch area */ +#define MIPI_DSI_MODE_VIDEO_HBP BIT(6) +/* disable hsync-active area */ +#define MIPI_DSI_MODE_VIDEO_HSA BIT(7) +/* flush display FIFO on vsync pulse */ +#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) +/* disable EoT packets in HS mode */ +#define MIPI_DSI_MODE_EOT_PACKET BIT(9) +/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ +#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) +/* transmit data in low power */ +#define MIPI_DSI_MODE_LPM BIT(11) + +enum mipi_dsi_pixel_format { + MIPI_DSI_FMT_RGB888, + MIPI_DSI_FMT_RGB666, + MIPI_DSI_FMT_RGB666_PACKED, + MIPI_DSI_FMT_RGB565, +}; + +#define DSI_DEV_NAME_SIZE 20 + +/** + * struct mipi_dsi_device_info - template for creating a mipi_dsi_device + * @type: DSI peripheral chip type + * @channel: DSI virtual channel assigned to peripheral + * @node: pointer to OF device node or NULL + * + * This is populated and passed to mipi_dsi_device_new to create a new + * DSI device + */ +struct mipi_dsi_device_info { + char type[DSI_DEV_NAME_SIZE]; + u32 channel; + struct device_node *node; +}; + +/** + * struct mipi_dsi_device - DSI peripheral device + * @host: DSI host for this peripheral + * @dev: driver model device node for this peripheral + * @name: DSI peripheral chip type + * @channel: virtual channel assigned to the peripheral + * @format: pixel format for video mode + * @lanes: number of active data lanes + * @mode_flags: DSI operation mode related flags + */ +struct mipi_dsi_device { + struct mipi_dsi_host *host; + struct udevice *dev; + + char name[DSI_DEV_NAME_SIZE]; + unsigned int channel; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; +}; + +/** + * mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any + * given pixel format defined by the MIPI DSI + * specification + * @fmt: MIPI DSI pixel format + * + * Returns: The number of bits per pixel of the given pixel format. + */ +static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt) +{ + switch (fmt) { + case MIPI_DSI_FMT_RGB888: + case MIPI_DSI_FMT_RGB666: + return 24; + + case MIPI_DSI_FMT_RGB666_PACKED: + return 18; + + case MIPI_DSI_FMT_RGB565: + return 16; + } + + return -EINVAL; +} + +/** + * struct mipi_dsi_panel_plat - DSI panel platform data + * @device: DSI peripheral device + * @lanes: number of active data lanes + * @format: pixel format for video mode + * @mode_flags: DSI operation mode related flags + */ +struct mipi_dsi_panel_plat { + struct mipi_dsi_device *device; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; +}; + +/** + * mipi_dsi_attach - attach a DSI device to its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_attach(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_detach - detach a DSI device from its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_detach(struct mipi_dsi_device *dsi); +int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi); +int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi); +int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi, + u16 value); + +ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload, + size_t size); +ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params, + size_t num_params, void *data, size_t size); + +/** + * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode + * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking + * information only + * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both + * V-Blanking and H-Blanking information + */ +enum mipi_dsi_dcs_tear_mode { + MIPI_DSI_DCS_TEAR_MODE_VBLANK, + MIPI_DSI_DCS_TEAR_MODE_VHBLANK, +}; + +#define MIPI_DSI_DCS_POWER_MODE_DISPLAY BIT(2) +#define MIPI_DSI_DCS_POWER_MODE_NORMAL BIT(3) +#define MIPI_DSI_DCS_POWER_MODE_SLEEP BIT(4) +#define MIPI_DSI_DCS_POWER_MODE_PARTIAL BIT(5) +#define MIPI_DSI_DCS_POWER_MODE_IDLE BIT(6) + +/** + * mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload + * @dsi: DSI peripheral device + * @data: buffer containing data to be transmitted + * @len: size of transmission buffer + * + * This function will automatically choose the right data type depending on + * the command payload length. + * + * Return: The number of bytes successfully transmitted or a negative error + * code on failure. + */ +ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi, + const void *data, size_t len); + +/** + * mipi_dsi_dcs_write() - send DCS write command + * @dsi: DSI peripheral device + * @cmd: DCS command + * @data: buffer containing the command payload + * @len: command payload length + * + * This function will automatically choose the right data type depending on + * the command payload length. + + * code on failure. + */ +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, + const void *data, size_t len); + +/** + * mipi_dsi_dcs_read() - send DCS read request command + * @dsi: DSI peripheral device + * @cmd: DCS command + * @data: buffer in which to receive data + * @len: size of receive buffer + * + * Return: The number of bytes read or a negative error code on failure. + */ +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len); + +/** + * mipi_dsi_dcs_nop() - send DCS nop packet + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_soft_reset() - perform a software reset of the display module + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_get_power_mode() - query the display module's current power + * mode + * @dsi: DSI peripheral device + * @mode: return location for the current power mode + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode); + +/** + * mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: return location for the pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format); + +/** + * mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the + * display module except interface communication + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display + * module + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_set_display_off() - stop displaying the image data on the + * display device + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_set_display_on() - start displaying the image data on the + * display device + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_set_column_address() - define the column extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first column of frame memory + * @end: last column of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, + u16 end); +/** + * mipi_dsi_dcs_set_page_address() - define the page extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first page of frame memory + * @end: last page of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, + u16 end); + +/** + * mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect + * output signal on the TE signal line + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi); + +/** + * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect + * output signal on the TE signal line. + * @dsi: DSI peripheral device + * @mode: the Tearing Effect Output Line mode + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, + enum mipi_dsi_dcs_tear_mode mode); + +/** + * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); + +/** + * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for + * the Tearing Effect output signal of the display module + * @dsi: DSI peripheral device + * @scanline: scanline to use as trigger + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline); + +/** + * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the + * display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, + u16 brightness); + +/** + * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value + * of the display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, + u16 *brightness); + +#endif /* MIPI_DSI_H */ diff --git a/include/mmc.h b/include/mmc.h index 95548e9..a3974a8 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -461,6 +461,16 @@ struct dm_mmc_ops { */ int (*wait_dat0)(struct udevice *dev, int state, int timeout); #endif + + /** + * host_power_cycle - host specific tasks in power cycle sequence + * Called between mmc_power_off() and + * mmc_power_on() + * + * @dev: Device to check + * @return 0 if not present, 1 if present, -ve on error + */ + int (*host_power_cycle)(struct udevice *dev); }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -473,6 +483,7 @@ int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev); int dm_mmc_execute_tuning(struct udevice *dev, uint opcode); int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout); +int dm_mmc_host_power_cycle(struct udevice *dev); /* Transition functions for compatibility */ int mmc_set_ios(struct mmc *mmc); @@ -481,6 +492,7 @@ int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc); int mmc_execute_tuning(struct mmc *mmc, uint opcode); int mmc_wait_dat0(struct mmc *mmc, int state, int timeout); +int mmc_host_power_cycle(struct mmc *mmc); #else struct mmc_ops { @@ -490,6 +502,7 @@ struct mmc_ops { int (*init)(struct mmc *mmc); int (*getcd)(struct mmc *mmc); int (*getwp)(struct mmc *mmc); + int (*host_power_cycle)(struct mmc *mmc); }; #endif diff --git a/include/power/stpmic1.h b/include/power/stpmic1.h index d90a1a9..b5a6d52 100644 --- a/include/power/stpmic1.h +++ b/include/power/stpmic1.h @@ -37,6 +37,7 @@ #define STPMIC1_BUCK_VOUT(sel) (sel << STPMIC1_BUCK_VOUT_SHIFT) #define STPMIC1_BUCK2_1200000V STPMIC1_BUCK_VOUT(24) +#define STPMIC1_BUCK2_1250000V STPMIC1_BUCK_VOUT(26) #define STPMIC1_BUCK2_1350000V STPMIC1_BUCK_VOUT(30) #define STPMIC1_BUCK3_1800000V STPMIC1_BUCK_VOUT(39) diff --git a/include/remoteproc.h b/include/remoteproc.h index fa82531..e25702b 100644 --- a/include/remoteproc.h +++ b/include/remoteproc.h @@ -45,6 +45,17 @@ struct dm_rproc_uclass_pdata { }; /** + * struct rproc_priv - Device information used by the rproc uclass + * + * @rsc_table_addr: Resource Table address (if any) + * @rsc_table_size: Resource Table size + */ +struct rproc_priv { + ulong rsc_table_addr; + unsigned int rsc_table_size; +}; + +/** * struct dm_rproc_ops - Operations that are provided by remote proc driver * @init: Initialize the remoteproc device invoked after probe (optional) * Return 0 on success, -ve error on fail -- 2.7.4