From 3c34681dd3fe562d5d65d83229caa921afc67a1f Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Wed, 11 Apr 2018 17:03:45 +0300 Subject: Add QSPI support and Hyperflash devicetree support. --- .../0110-Renesas-clk-Add-RPC-clock-source.patch | 263 ++++ .../0111-Renesas-r8a7798-Add-RPC-clock.patch | 46 + ...nesas-r8a7798-pinctrl-Add-RPC-pin-control.patch | 119 ++ .../0113-Renesas-RPC-Add-RPC-driver.patch | 1483 ++++++++++++++++++++ .../0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch | 35 + .../0115-R8A7798-condor-dts-Add-qspi-flash.patch | 98 ++ .../0116-spi-nor-Add-s25fs512-flash-support.patch | 126 ++ .../0117-spi-nor-Add-flash-array-support.patch | 89 ++ .../0118-r8a7797-clk-Add-rpc-clock.patch | 45 + .../0119-r8a7797-pinctrl-Add-qspi-pins.patch | 118 ++ .../0120-r8a7797-dtsi-Add-rpc-node.patch | 35 + ...0121-r8a7797-eagle-dts-Add-spi-flash-node.patch | 96 ++ ...sk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch | 172 +++ .../0123-V3HSK-dts-Add-qspi-node.patch | 101 ++ ...124-RPC-Hyperflash-Add-devicetree-support.patch | 219 +++ ...7-pinctrl-Add-pin-function-for-hyperflash.patch | 70 + ...8-pinctrl-Add-pin-function-for-hyperflash.patch | 69 + .../recipes-kernel/linux/linux-renesas/qspi.cfg | 3 + .../linux/linux-renesas_4.9.bbappend | 18 + 19 files changed, 3205 insertions(+) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch new file mode 100644 index 0000000..37d08e9 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch @@ -0,0 +1,263 @@ +From b4acae6eda19307e0051763a532f522006a82d5c Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 10:37:04 +0300 +Subject: [PATCH 01/12] Renesas: clk: Add RPC clock source + +Add RPC clock source functionality + +Signed-off-by: Dmitry Shifrin +--- + drivers/clk/renesas/rcar-gen3-cpg.c | 200 ++++++++++++++++++++++++++++++++++++ + drivers/clk/renesas/rcar-gen3-cpg.h | 4 + + 2 files changed, 204 insertions(+) + +diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c +index 99acba2..969e1b2 100644 +--- a/drivers/clk/renesas/rcar-gen3-cpg.c ++++ b/drivers/clk/renesas/rcar-gen3-cpg.c +@@ -599,6 +599,7 @@ static const struct clk_div_table cpg_sd01_div_table[] = { + { 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 }, + }; + ++ + #define CPG_SD_STP_HCK BIT(9) + #define CPG_SD_STP_CK BIT(8) + +@@ -811,6 +812,202 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, + return clk; + } + ++/**************** RPCSCK ***********************************/ ++ ++ ++ ++struct rpc_clock { ++ struct clk_hw hw; ++ void __iomem *reg; ++ const struct clk_div_table *div_table; ++ unsigned int div_num; ++ unsigned int div_min; ++ unsigned int div_max; ++}; ++ ++static const struct clk_div_table cpg_rpc_div_table[] = { ++ { 0x11, 20 }, { 0x13, 40 }, { 0x15, 60 }, { 0x17, 80 }, ++ { 0x19, 24 }, { 0x1B, 48 }, { 0x1D, 72 }, { 0x1F, 96 }, ++}; ++ ++#define to_rpc_clock(_hw) container_of(_hw, struct rpc_clock, hw) ++ ++#define CPG_RPC_ENA_MASK (BIT(9) | BIT(8)) ++#define CPG_RPC_SCALE_MASK (0x1F) ++ ++ ++static int cpg_rpc_clock_endisable(void __iomem *reg, bool enable) ++{ ++ u32 val; ++ ++ val = readl(reg); ++ ++ if (enable) ++ val &= ~CPG_RPC_ENA_MASK; ++ else ++ val |= CPG_RPC_ENA_MASK; ++ ++ writel(val, reg); ++ return 0; ++}; ++ ++static int cpg_rpc_clock_enable(struct clk_hw *hw) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ ++ cpg_rpc_clock_endisable(clock->reg, true); ++ ++ return 0; ++} ++ ++static void cpg_rpc_clock_disable(struct clk_hw *hw) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ ++ cpg_rpc_clock_endisable(clock->reg, false); ++} ++ ++static int cpg_rpc_clock_is_enabled(struct clk_hw *hw) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ ++ return !(readl(clock->reg) & CPG_RPC_ENA_MASK); ++} ++ ++static unsigned long cpg_rpc_clock_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ unsigned long rate = parent_rate; ++ u32 val, scale; ++ unsigned int i; ++ ++ val = readl(clock->reg); ++ ++ scale = val & CPG_RPC_SCALE_MASK; ++ for (i = 0; i < clock->div_num; i++) ++ if (scale == clock->div_table[i].val) ++ break; ++ ++ if (i >= clock->div_num) ++ return 0; ++ ++ return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div); ++} ++ ++static unsigned int cpg_rpc_clock_calc_div(struct rpc_clock *clock, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ unsigned int div; ++ ++ if (!rate) ++ rate = 1; ++ ++ div = DIV_ROUND_CLOSEST(parent_rate, rate); ++ ++ return clamp_t(unsigned int, div, clock->div_min, clock->div_max); ++} ++ ++static long cpg_rpc_clock_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ unsigned int div = cpg_rpc_clock_calc_div(clock, rate, *parent_rate); ++ ++ return DIV_ROUND_CLOSEST(*parent_rate, div); ++} ++ ++static int cpg_rpc_clock_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct rpc_clock *clock = to_rpc_clock(hw); ++ unsigned int div = cpg_rpc_clock_calc_div(clock, rate, parent_rate); ++ u32 val; ++ unsigned int i, nearest_i, nearest_div; ++ ++ ++ for (i = 0; i < clock->div_num; i++) ++ if (div == clock->div_table[i].div) ++ break; ++ ++ if (i >= clock->div_num) { ++ /* find nearest */ ++ nearest_div = -1; ++ nearest_i = clock->div_num; ++ for (i = 0; i < clock->div_num; i++) { ++ if (clock->div_table[i].div >= div && ++ clock->div_table[i].div <= nearest_div) { ++ nearest_i = i; ++ nearest_div = clock->div_table[i].div; ++ } ++ } ++ ++ i = nearest_i; ++ } ++ ++ if (i >= clock->div_num) ++ return -EINVAL; ++ ++ val = readl(clock->reg); ++ val &= ~CPG_RPC_SCALE_MASK; ++ val |= clock->div_table[i].val; ++ writel(val, clock->reg); ++ ++ return 0; ++} ++ ++static const struct clk_ops cpg_rpc_clock_ops = { ++ .enable = cpg_rpc_clock_enable, ++ .disable = cpg_rpc_clock_disable, ++ .is_enabled = cpg_rpc_clock_is_enabled, ++ .recalc_rate = cpg_rpc_clock_recalc_rate, ++ .round_rate = cpg_rpc_clock_round_rate, ++ .set_rate = cpg_rpc_clock_set_rate, ++}; ++ ++static struct clk * __init cpg_rpc_clk_register(const struct cpg_core_clk *core, ++ void __iomem *base, ++ const char *parent_name) ++{ ++ struct clk_init_data init; ++ struct rpc_clock *clock; ++ struct clk *clk; ++ unsigned int i; ++ ++ clock = kzalloc(sizeof(*clock), GFP_KERNEL); ++ if (!clock) ++ return ERR_PTR(-ENOMEM); ++ ++ init.name = core->name; ++ init.ops = &cpg_rpc_clock_ops; ++ init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; ++ init.parent_names = &parent_name; ++ init.num_parents = 1; ++ ++ clock->reg = base + core->offset; ++ clock->hw.init = &init; ++ clock->div_table = cpg_rpc_div_table; ++ clock->div_num = ARRAY_SIZE(cpg_rpc_div_table); ++ ++ clock->div_max = clock->div_table[0].div; ++ clock->div_min = clock->div_max; ++ for (i = 1; i < clock->div_num; i++) { ++ clock->div_max = max(clock->div_max, clock->div_table[i].div); ++ clock->div_min = min(clock->div_min, clock->div_table[i].div); ++ } ++ ++ clk = clk_register(NULL, &clock->hw); ++ if (IS_ERR(clk)) ++ kfree(clock); ++ ++ return clk; ++} ++ ++/***********************************************************/ ++ ++ ++ + + static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata; + static unsigned int cpg_clk_extalr __initdata; +@@ -943,6 +1140,9 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, + return cpg_zg_clk_register(core->name, __clk_get_name(parent), + base); + ++ case CLK_TYPE_GEN3_RPCSRC: ++ return cpg_rpc_clk_register(core, base, __clk_get_name(parent)); ++ + default: + return ERR_PTR(-EINVAL); + } +diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h +index 694bedc..636ef54 100644 +--- a/drivers/clk/renesas/rcar-gen3-cpg.h ++++ b/drivers/clk/renesas/rcar-gen3-cpg.h +@@ -27,6 +27,7 @@ enum rcar_gen3_clk_types { + CLK_TYPE_GEN3_ZG, + CLK_TYPE_GEN3_RINT, + CLK_TYPE_GEN3_OSC, ++ CLK_TYPE_GEN3_RPCSRC, + }; + + #define DEF_GEN3_SD(_name, _id, _parent, _offset) \ +@@ -36,6 +37,9 @@ enum rcar_gen3_clk_types { + #define DEF_GEN3_SD0H(_name, _id, _parent, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD0H, _parent, .offset = _offset) + ++#define DEF_GEN3_RPCSRC(_name, _id, _parent, _offset) \ ++ DEF_BASE(_name, _id, CLK_TYPE_GEN3_RPCSRC, _parent, .offset = _offset) ++ + struct rcar_gen3_cpg_pll_config { + unsigned int extal_div; + unsigned int pll1_mult; +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch new file mode 100644 index 0000000..e0fb7d2 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0111-Renesas-r8a7798-Add-RPC-clock.patch @@ -0,0 +1,46 @@ +From 05ab83db0e0012674a0255fe37a51713a1601732 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 10:42:50 +0300 +Subject: [PATCH 02/12] Renesas: r8a7798: Add RPC clock + +Signed-off-by: Dmitry Shifrin +--- + drivers/clk/renesas/r8a7798-cpg-mssr.c | 3 +++ + include/dt-bindings/clock/r8a7798-cpg-mssr.h | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/drivers/clk/renesas/r8a7798-cpg-mssr.c b/drivers/clk/renesas/r8a7798-cpg-mssr.c +index e407916..2614147 100644 +--- a/drivers/clk/renesas/r8a7798-cpg-mssr.c ++++ b/drivers/clk/renesas/r8a7798-cpg-mssr.c +@@ -90,6 +90,8 @@ static const struct cpg_core_clk r8a7798_core_clks[] __initconst = { + + DEF_GEN3_SD("sd0", R8A7798_CLK_SD0, CLK_SDSRC, 0x0074), /* OK? */ + ++ DEF_GEN3_RPCSRC("rpcsrc", R8A7798_CLK_RPCSRC, CLK_PLL1, 0x0238), ++ + DEF_FIXED("cl", R8A7798_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cp", R8A7798_CLK_CP, CLK_EXTAL, 2, 1), + +@@ -194,6 +196,7 @@ static const struct mssr_mod_clk r8a7798_mod_clks[] __initconst = { + DEF_MOD("gpio1", 911, R8A7798_CLK_CP), + DEF_MOD("gpio0", 912, R8A7798_CLK_CP), + DEF_MOD("can-fd", 914, R8A7798_CLK_S3D2), ++ DEF_MOD("rpc", 917, R8A7798_CLK_RPCSRC), + /* FIXME missing MSSR for i2c5; should it be 919 as in H3/M3? */ + /* DEF_MOD("i2c4", 919, R8A7798_CLK_S3D2), */ + DEF_MOD("i2c4", 927, R8A7798_CLK_S0D6), +diff --git a/include/dt-bindings/clock/r8a7798-cpg-mssr.h b/include/dt-bindings/clock/r8a7798-cpg-mssr.h +index 3d85730..821383d3 100644 +--- a/include/dt-bindings/clock/r8a7798-cpg-mssr.h ++++ b/include/dt-bindings/clock/r8a7798-cpg-mssr.h +@@ -52,5 +52,6 @@ + #define R8A7798_CLK_CPEX 36 + #define R8A7798_CLK_R 37 + #define R8A7798_CLK_OSC 38 ++#define R8A7798_CLK_RPCSRC 39 + + #endif /* __DT_BINDINGS_CLOCK_R8A7798_CPG_MSSR_H__ */ +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch new file mode 100644 index 0000000..5c91f56 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch @@ -0,0 +1,119 @@ +From ba6a829c33809d780c40c3f60e97e8a9e2a99a92 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 10:48:44 +0300 +Subject: [PATCH 03/12] Renesas: r8a7798: pinctrl: Add RPC pin control + +Signed-off-by: Dmitry Shifrin +--- + drivers/pinctrl/sh-pfc/pfc-r8a7798.c | 74 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 74 insertions(+) + +diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c +index 39aba74..f20afd1 100644 +--- a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c ++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c +@@ -1472,6 +1472,60 @@ static const unsigned int scif_clk_b_mux[] = { + SCIF_CLK_B_MARK, + }; + ++/* - QSPI ------------------------------------------------------------------- */ ++static const unsigned int qspi0_ctrl_pins[] = { ++ /* QSPI0_SPCLK QSPI0_SSL */ ++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 0), ++}; ++static const unsigned int qspi0_ctrl_mux[] = { ++ QSPI0_SPCLK_MARK, QSPI0_SSL_MARK, ++}; ++ ++static const unsigned int qspi0_data2_pins[] = { ++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1 */ ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2), ++}; ++static const unsigned int qspi0_data2_mux[] = { ++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK, ++}; ++ ++static const unsigned int qspi0_data4_pins[] = { ++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1, QSPI0_IO2, QSPI0_IO3 */ ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2), ++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 4), ++}; ++static const unsigned int qspi0_data4_mux[] = { ++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK, ++ QSPI0_IO2_MARK, QSPI0_IO3_MARK ++}; ++ ++static const unsigned int qspi1_ctrl_pins[] = { ++ /* QSPI1_SPCLK QSPI1_SSL */ ++ RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 11), ++}; ++static const unsigned int qspi1_ctrl_mux[] = { ++ QSPI1_SPCLK_MARK, QSPI1_SSL_MARK, ++}; ++ ++static const unsigned int qspi1_data2_pins[] = { ++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1 */ ++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8), ++}; ++static const unsigned int qspi1_data2_mux[] = { ++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK, ++}; ++ ++static const unsigned int qspi1_data4_pins[] = { ++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1, QSPI1_IO2, QSPI1_IO3 */ ++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8), ++ RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 10), ++}; ++static const unsigned int qspi1_data4_mux[] = { ++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK, ++ QSPI1_IO2_MARK, QSPI1_IO3_MARK ++}; ++ ++ + /* - I2C -------------------------------------------------------------------- */ + static const unsigned int i2c0_pins[] = { + /* SDA0, SCL0 */ +@@ -2419,6 +2473,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(vin1_field), + SH_PFC_PIN_GROUP(vin1_clkenb), + SH_PFC_PIN_GROUP(vin1_clk), ++ SH_PFC_PIN_GROUP(qspi0_ctrl), ++ SH_PFC_PIN_GROUP(qspi0_data2), ++ SH_PFC_PIN_GROUP(qspi0_data4), ++ SH_PFC_PIN_GROUP(qspi1_ctrl), ++ SH_PFC_PIN_GROUP(qspi1_data2), ++ SH_PFC_PIN_GROUP(qspi1_data4), + }; + + static const char * const avb_groups[] = { +@@ -2683,6 +2743,18 @@ static const char * const vin1_groups[] = { + "vin1_clk", + }; + ++static const char * const qspi0_groups[] = { ++ "qspi0_ctrl", ++ "qspi0_data2", ++ "qspi0_data4", ++}; ++ ++static const char * const qspi1_groups[] = { ++ "qspi1_ctrl", ++ "qspi1_data2", ++ "qspi1_data4", ++}; ++ + static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(avb), + SH_PFC_FUNCTION(gether), +@@ -2719,6 +2791,8 @@ static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(tmu), + SH_PFC_FUNCTION(vin0), + SH_PFC_FUNCTION(vin1), ++ SH_PFC_FUNCTION(qspi0), ++ SH_PFC_FUNCTION(qspi1), + }; + + static const struct pinmux_cfg_reg pinmux_config_regs[] = { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch new file mode 100644 index 0000000..86b2d6c --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0113-Renesas-RPC-Add-RPC-driver.patch @@ -0,0 +1,1483 @@ +From 931484f9bd4eb1741588dc1574080fa4534ac5dc Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 11:08:16 +0300 +Subject: [PATCH 04/12] Renesas: RPC: Add RPC driver + +This is initial commit. Driver supports single and dual mode. +Dual mode means that there are two spi-nor flashes connected. +Supports qspi flashes. + +Signed-off-by: Dmitry Shifrin +--- + drivers/mtd/spi-nor/Kconfig | 8 + + drivers/mtd/spi-nor/Makefile | 1 + + drivers/mtd/spi-nor/renesas-rpc.c | 1429 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 1438 insertions(+) + create mode 100644 drivers/mtd/spi-nor/renesas-rpc.c + +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 4a682ee..298dc1d 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -7,6 +7,14 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++ ++config SPI_RENESAS_RPC ++ tristate "Renesas RPC controller" ++ depends on ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A7797 || ARCH_R8A7798 || COMPILE_TEST ++ help ++ QSPI driver for Renesas SPI Multi I/O Bus controller. This controller ++ supports normal, dual and quad spi for one or two SPI NOR Flashes. ++ + config MTD_MT81xx_NOR + tristate "Mediatek MT81xx SPI NOR flash controller" + depends on HAS_IOMEM +diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile +index 121695e..e99d775 100644 +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -5,3 +5,4 @@ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o + obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o + obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o + obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o ++obj-$(CONFIG_SPI_RENESAS_RPC) += renesas-rpc.o +diff --git a/drivers/mtd/spi-nor/renesas-rpc.c b/drivers/mtd/spi-nor/renesas-rpc.c +new file mode 100644 +index 0000000..1e93c98 +--- /dev/null ++++ b/drivers/mtd/spi-nor/renesas-rpc.c +@@ -0,0 +1,1429 @@ ++/* ++ * Renesas RPC driver ++ * ++ * Copyright (C) 2018, Cogent Embedded Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++ ++#define CMNCR 0x0000 ++#define SSLDR 0x0004 ++#define DRCR 0x000C ++#define DRCMR 0x0010 ++#define DREAR 0x0014 ++#define DROPR 0x0018 ++#define DRENR 0x001C ++#define SMCR 0x0020 ++#define SMCMR 0x0024 ++#define SMADR 0x0028 ++#define SMOPR 0x002C ++#define SMENR 0x0030 ++#define SMRDR0 0x0038 ++#define SMRDR1 0x003C ++#define SMWDR0 0x0040 ++#define SMWDR1 0x0044 ++#define CMNSR 0x0048 ++#define DRDMCR 0x0058 ++#define DRDRENR 0x005C ++#define SMDMCR 0x0060 ++#define SMDRENR 0x0064 ++#define PHYCNT 0x007C ++#define PHYOFFSET1 0x0080 ++#define PHYOFFSET2 0x0084 ++#define PHYINT 0x0088 ++#define DIV_REG 0x00A8 ++ ++/* CMNCR */ ++#define CMNCR_BSZ_MASK (0x03) ++#define CMNCR_BSZ_4x1 (0x0) ++#define CMNCR_BSZ_8x1 (0x1) ++#define CMNCR_BSZ_4x2 (0x1) ++#define CMNCR_MD (0x1 << 31) ++#define CMNCR_MOIIO3_MASK (0x3 << 22) ++#define CMNCR_MOIIO3_HIZ (0x3 << 22) ++#define CMNCR_MOIIO2_MASK (0x3 << 20) ++#define CMNCR_MOIIO2_HIZ (0x3 << 20) ++#define CMNCR_MOIIO1_MASK (0x3 << 18) ++#define CMNCR_MOIIO1_HIZ (0x3 << 18) ++#define CMNCR_MOIIO0_MASK (0x3 << 16) ++#define CMNCR_MOIIO0_HIZ (0x3 << 16) ++#define CMNCR_IO0FV_MASK (0x3 << 8) ++#define CMNCR_IO0FV_HIZ (0x3 << 8) ++ ++ ++/* DRCR */ ++#define DRCR_RBURST_MASK (0x1f << 16) ++#define DRCR_RBURST(v) (((v) & 0x1f) << 16) ++#define DRCR_SSLE (0x1) ++#define DRCR_RBE (0x1 << 8) ++#define DRCR_RCF (0x1 << 9) ++#define DRCR_RBURST_32 (0x1f << 16) ++ ++ ++/* SMENR */ ++#define SMENR_CDB_MASK (0x03 << 30) ++#define SMENR_CDB(v) (((v) & 0x03) << 30) ++#define SMENR_CDB_1B (0) ++#define SMENR_CDB_2B (0x1 << 30) ++#define SMENR_CDB_4B (0x2 << 30) ++#define SMENR_OCDB_MASK (0x03 << 28) ++#define SMENR_OCDB_1B (0) ++#define SMENR_OCDB_2B (0x1 << 28) ++#define SMENR_OCDB_4B (0x2 << 28) ++#define SMENR_ADB_MASK (0x03 << 24) ++#define SMENR_ADB(v) (((v) & 0x03) << 24) ++#define SMENR_ADB_1B (0) ++#define SMENR_ADB_2B (0x1 << 24) ++#define SMENR_ADB_4B (0x2 << 24) ++#define SMENR_OPDB_MASK (0x03 << 20) ++#define SMENR_OPDB_1B (0) ++#define SMENR_OPDB_2B (0x1 << 20) ++#define SMENR_OPDB_4B (0x2 << 20) ++#define SMENR_SPIDB_MASK (0x03 << 16) ++#define SMENR_SPIDB(v) (((v) & 0x03) << 16) ++#define SMENR_SPIDB_1B (0) ++#define SMENR_SPIDB_2B (0x1 << 16) ++#define SMENR_SPIDB_4B (0x2 << 16) ++#define SMENR_OPDE_MASK (0xf << 4) ++#define SMENR_OPDE_DISABLE (0) ++#define SMENR_OPDE3 (0x8 << 4) ++#define SMENR_OPDE32 (0xC << 4) ++#define SMENR_OPDE321 (0xE << 4) ++#define SMENR_OPDE3210 (0xF << 4) ++#define SMENR_SPIDE_MASK (0x0F) ++#define SMENR_SPIDE_DISABLE (0) ++#define SMENR_SPIDE_8B (0x08) ++#define SMENR_SPIDE_16B (0x0C) ++#define SMENR_SPIDE_32B (0x0F) ++#define SMENR_DME (1<<15) ++#define SMENR_CDE (1<<14) ++#define SMENR_OCDE (1<<12) ++#define SMENR_ADE_MASK (0xf << 8) ++#define SMENR_ADE_DISABLE (0) ++#define SMENR_ADE_23_16 (0x4 << 8) ++#define SMENR_ADE_23_8 (0x6 << 8) ++#define SMENR_ADE_23_0 (0x7 << 8) ++#define SMENR_ADE_31_0 (0xf << 8) ++ ++ ++/* SMCMR */ ++#define SMCMR_CMD(cmd) (((cmd) & 0xff) << 16) ++#define SMCMR_CMD_MASK (0xff << 16) ++#define SMCMR_OCMD(cmd) (((cmd) & 0xff)) ++#define SMCMR_OCMD_MASK (0xff) ++ ++ ++/* SMDRENR */ ++#define SMDRENR_HYPE_MASK (0x7 << 12) ++#define SMDRENR_HYPE_SPI_FLASH (0x0) ++#define SMDRENR_ADDRE (0x1 << 8) ++#define SMDRENR_OPDRE (0x1 << 4) ++#define SMDRENR_SPIDRE (0x1) ++ ++/* PHYCNT */ ++#define PHYCNT_CAL (0x1 << 31) ++#define PHYCNT_OCTA_MASK (0x3 << 22) ++#define PHYCNT_EXDS (0x1 << 21) ++#define PHYCNT_OCT (0x1 << 20) ++#define PHYCNT_DDRCAL (0x1 << 19) ++#define PHYCNT_HS (0x1 << 18) ++#define PHYCNT_STREAM_MASK (0x7 << 15) ++#define PHYCNT_STREAM(o) (((o) & 0x7) << 15) ++#define PHYCNT_WBUF2 (0x1 << 4) ++#define PHYCNT_WBUF (0x1 << 2) ++#define PHYCNT_PHYMEM_MASK (0x3) ++ ++/* SMCR */ ++#define SMCR_SSLKP (0x1 << 8) ++#define SMCR_SPIRE (0x1 << 2) ++#define SMCR_SPIWE (0x1 << 1) ++#define SMCR_SPIE (0x1) ++ ++/* CMNSR */ ++#define CMNSR_TEND (0x1 << 0) ++ ++/* SSLDR */ ++#define SSLDR_SPNDL(v) (((v) & 0x7) << 16) ++#define SSLDR_SLNDL(v) ((((v) | 0x4) & 0x7) << 8) ++#define SSLDR_SCKDL(v) ((v) & 0x7) ++ ++/* DREAR */ ++#define DREAR_EAV_MASK (0xff << 16) ++#define DREAR_EAV(v) (((v) & 0xff) << 16) ++#define DREAR_EAC_MASK (0x7) ++#define DREAR_24B (0) ++#define DREAR_25B (1) ++ ++/* DRENR */ ++#define DRENR_CDB_MASK (0x03 << 30) ++#define DRENR_CDB(v) (((v) & 0x3) << 30) ++#define DRENR_CDB_1B (0) ++#define DRENR_CDB_2B (0x1 << 30) ++#define DRENR_CDB_4B (0x2 << 30) ++#define DRENR_OCDB_MASK (0x03 << 28) ++#define DRENR_OCDB_1B (0) ++#define DRENR_OCDB_2B (0x1 << 28) ++#define DRENR_OCDB_4B (0x2 << 28) ++#define DRENR_ADB_MASK (0x03 << 24) ++#define DRENR_ADB(v) (((v) & 0x3) << 24) ++#define DRENR_ADB_1B (0) ++#define DRENR_ADB_2B (0x1 << 24) ++#define DRENR_ADB_4B (0x2 << 24) ++#define DRENR_OPDB_MASK (0x03 << 20) ++#define DRENR_OPDB_1B (0) ++#define DRENR_OPDB_2B (0x1 << 20) ++#define DRENR_OPDB_4B (0x2 << 20) ++#define DRENR_DRDB_MASK (0x03 << 16) ++#define DRENR_DRDB(v) (((v) & 0x3) << 16) ++#define DRENR_DRDB_1B (0) ++#define DRENR_DRDB_2B (0x1 << 16) ++#define DRENR_DRDB_4B (0x2 << 16) ++#define DRENR_OPDE_MASK (0xf << 4) ++#define DRENR_OPDE_DISABLE (0) ++#define DRENR_OPDE3 (0x8 << 4) ++#define DRENR_OPDE32 (0xC << 4) ++#define DRENR_OPDE321 (0xE << 4) ++#define DRENR_OPDE3210 (0xF << 4) ++#define DRENR_DME (1<<15) ++#define DRENR_CDE (1<<14) ++#define DRENR_OCDE (1<<12) ++#define DRENR_ADE_MASK (0xf << 8) ++#define DRENR_ADE_DISABLE (0) ++#define DRENR_ADE_23_0 (0x7 << 8) ++#define DRENR_ADE_31_0 (0xf << 8) ++ ++/* DRCMR */ ++#define DRCMR_CMD(cmd) (((cmd) & 0xff) << 16) ++#define DRCMR_CMD_MASK (0xff << 16) ++#define DRCMR_OCMD(cmd) (((cmd) & 0xff)) ++#define DRCMR_OCMD_MASK (0xff) ++ ++/* DRCMR */ ++#define DRDMCR_DMCYC(v) ((v) & 0x1f) ++#define DRDMCR_DMCYC_MASK (0x1f) ++ ++/* SMDMCR */ ++#define SMDMCR_DMCYC(v) ((v) & 0x0f) ++#define SMDMCR_DMCYC_MASK (0x0f) ++ ++/* PHYOFFSET1 */ ++#define PHYOFFSET1_DDRTMG (1 << 28) ++ ++/* DIVREG */ ++#define DIVREG_RATIO_MASK (0x03) ++#define DIVREG_RATIO(v) ((v) & 0x03) ++#define DIVREG_RATIO_MAX (0x2) ++ ++ ++#define DEFAULT_TO (100) ++#define WRITE_BUF_SIZE (0x100) ++#define WRITE_BUF_ADR_MASK (0xff) ++ ++#define REPEAT_MAX (20) ++#define REPEAT_TIME (10) ++ ++ ++struct rpc_spi { ++ struct platform_device *pdev; ++ void __iomem *base; ++ void __iomem *read_area; ++ void __iomem *write_area; ++ struct clk *clk; ++ unsigned int irq; ++ struct spi_nor spi_nor; ++ ++#define MTD_QSPI_1x 0 ++#define MTD_QSPI_2x 1 ++ ++ u32 mtdtype; ++}; ++ ++ ++ ++/* IP block use it's own clock divigion register */ ++#define OWN_CLOCK_DIVIDER BIT(0) ++ ++ ++ ++static void regs_dump(struct rpc_spi *rpc) ++{ ++ static u32 regs[] = { ++ CMNCR, SSLDR, DRCR, DRCMR, DREAR, ++ DROPR, DRENR, SMCR, SMCMR, SMADR, ++ SMOPR, SMENR, SMRDR0, SMRDR1, SMWDR0, ++ SMWDR1, CMNSR, DRDMCR, DRDRENR, SMDMCR, ++ SMDRENR, PHYCNT, PHYOFFSET1, PHYOFFSET2, ++ PHYINT ++ }; ++ ++ static const char *const names[] = { ++ "CMNCR", "SSLDR", "DRCR", "DRCMR", "DREAR", ++ "DROPR", "DRENR", "SMCR", "SMCMR", "SMADR", ++ "SMOPR", "SMENR", "SMRDR0", "SMRDR1", "SMWDR0", ++ "SMWDR1", "CMNSR", "DRDMCR", "DRDRENR", "SMDMCR", ++ "SMDRENR", "PHYCNT", "PHYOFFSET1", "PHYOFFSET2", ++ "PHYINT" ++ }; ++ ++ int i; ++ ++ dev_dbg(&rpc->pdev->dev, "RPC regs dump:\n"); ++ for (i = 0; i < ARRAY_SIZE(regs); i++) ++ dev_dbg(&rpc->pdev->dev, "%s = 0x%08x\n", names[i], ++ readl(rpc->base + regs[i])); ++} ++ ++ ++ ++ ++ ++static u32 rpc_read(struct rpc_spi *rpc, unsigned int reg) ++{ ++ u32 val; ++ ++ val = readl(rpc->base + reg); ++ return val; ++} ++ ++static void rpc_write(struct rpc_spi *rpc, unsigned int reg, u32 val) ++{ ++ writel(val, rpc->base + reg); ++} ++ ++ ++static int rpc_wait(struct rpc_spi *rpc, u32 to) ++{ ++ u32 val; ++ int i; ++ ++ for (i = 0; i < to; i++) { ++ val = rpc_read(rpc, CMNSR); ++ val &= CMNSR_TEND; ++ if (val) ++ break; ++ ++ udelay(100); ++ } ++ ++ if (i == to) { ++ dev_err(&rpc->pdev->dev, "timeout waiting for operation end %d\n", ++ rpc_read(rpc, CMNSR)); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++static int rpc_setup_clk_ratio(struct rpc_spi *rpc, u32 max_clk_rate) ++{ ++ unsigned long rate = clk_get_rate(rpc->clk); ++ u32 ratio; ++ u32 val; ++ ++ ratio = DIV_ROUND_UP(rate, max_clk_rate * 2) >> 1; ++ if (ratio > DIVREG_RATIO_MAX) ++ ratio = DIVREG_RATIO_MAX; ++ ++ val = rpc_read(rpc, DIV_REG); ++ val &= DIVREG_RATIO_MASK; ++ val |= DIVREG_RATIO(ratio); ++ rpc_write(rpc, DIV_REG, val); ++ ++ return 0; ++} ++ ++static int rpc_endisable_write_buf(struct rpc_spi *rpc, bool en) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, PHYCNT); ++ ++ if (en) ++ val |= PHYCNT_WBUF | PHYCNT_WBUF2; ++ else ++ val &= ~(PHYCNT_WBUF | PHYCNT_WBUF2); ++ ++ rpc_write(rpc, PHYCNT, val); ++ ++ return 0; ++} ++ ++ ++static int rpc_begin(struct rpc_spi *rpc, ++ bool rx, bool tx, bool last) ++{ ++ u32 val = SMCR_SPIE; ++ ++ if (rx) ++ val |= SMCR_SPIRE; ++ ++ if (tx) ++ val |= SMCR_SPIWE; ++ ++ if (!last) ++ val |= SMCR_SSLKP; ++ ++ rpc_write(rpc, SMCR, val); ++ ++ return 0; ++} ++ ++ ++static int rpc_setup_reg_mode(struct rpc_spi *rpc) ++{ ++ u32 val; ++ ++ rpc_wait(rpc, DEFAULT_TO); ++ ++ rpc_endisable_write_buf(rpc, false); ++ ++ /* ...setup manual mode */ ++ val = rpc_read(rpc, CMNCR); ++ val |= CMNCR_MD; ++ rpc_write(rpc, CMNCR, val); ++ ++ /* disable ddr */ ++ val = rpc_read(rpc, SMDRENR); ++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE); ++ rpc_write(rpc, SMDRENR, val); ++ ++ /* enable 1bit command */ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDB_MASK | SMENR_OCDB_MASK | SMENR_DME ++ | SMENR_OCDE | SMENR_SPIDB_MASK ++ | SMENR_ADE_MASK | SMENR_ADB_MASK ++ | SMENR_OPDE_MASK | SMENR_SPIDE_MASK); ++ val |= SMENR_CDB_1B | SMENR_CDE | SMENR_SPIDE_32B; ++ rpc_write(rpc, SMENR, val); ++ ++ ++ return 0; ++} ++ ++static void rpc_flush_cache(struct rpc_spi *rpc) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, DRCR); ++ val |= DRCR_RCF; ++ ++ rpc_write(rpc, DRCR, val); ++} ++ ++ ++static int rpc_setup_ext_mode(struct rpc_spi *rpc) ++{ ++ u32 val; ++ u32 cmncr; ++ ++ rpc_wait(rpc, DEFAULT_TO); ++ ++ rpc_endisable_write_buf(rpc, false); ++ ++ /* ...setup ext mode */ ++ val = rpc_read(rpc, CMNCR); ++ cmncr = val; ++ val &= ~(CMNCR_MD); ++ rpc_write(rpc, CMNCR, val); ++ ++ /* ...enable burst and clear cache */ ++ val = rpc_read(rpc, DRCR); ++ val &= ~(DRCR_RBURST_MASK | DRCR_RBE | DRCR_SSLE); ++ val |= DRCR_RBURST(0x1f) | DRCR_RBE; ++ ++ if (cmncr & CMNCR_MD) ++ val |= DRCR_RCF; ++ ++ rpc_write(rpc, DRCR, val); ++ ++ return 0; ++} ++ ++ ++static int rpc_setup_data_size(struct rpc_spi *rpc, u32 size, bool copy) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_SPIDE_MASK); ++ ++ if (rpc->mtdtype == MTD_QSPI_2x && !copy) ++ size >>= 1; ++ ++ switch (size) { ++ case 0: ++ break; ++ case 1: ++ val |= SMENR_SPIDE_8B; ++ break; ++ case 2: ++ val |= SMENR_SPIDE_16B; ++ break; ++ case 4: ++ val |= SMENR_SPIDE_32B; ++ break; ++ default: ++ dev_err(&rpc->pdev->dev, "Unsupported data width %d\n", size); ++ return -EINVAL; ++ } ++ rpc_write(rpc, SMENR, val); ++ ++ return 0; ++} ++ ++ ++static int rpc_setup_extmode_read_addr(struct rpc_spi *rpc, ++ int adr_width, loff_t adr) ++{ ++ u32 val; ++ u32 v; ++ ++ val = rpc_read(rpc, DREAR); ++ val &= ~(DREAR_EAV_MASK | DREAR_EAC_MASK); ++ ++ if (adr_width == 4) { ++ v = adr >> 25; ++ val |= DREAR_EAV(v) | DREAR_25B; ++ } ++ rpc_write(rpc, DREAR, val); ++ ++ val = rpc_read(rpc, DRENR); ++ val &= ~(DRENR_ADE_MASK); ++ if (adr_width == 4) ++ val |= DRENR_ADE_31_0; ++ else ++ val |= DRENR_ADE_23_0; ++ rpc_write(rpc, DRENR, val); ++ ++ return 0; ++} ++ ++ ++ ++#define NBITS_TO_VAL(v) ((v >> 1) & 3) ++static int rpc_setup_extmode_nbits(struct rpc_spi *rpc, int cnb, ++ int anb, int dnb) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, DRENR); ++ val &= ~(DRENR_CDB_MASK | DRENR_ADB_MASK | DRENR_DRDB_MASK); ++ val |= DRENR_CDB(NBITS_TO_VAL(cnb)) ++ | DRENR_ADB(NBITS_TO_VAL(anb)) ++ | DRENR_DRDB(NBITS_TO_VAL(dnb)); ++ rpc_write(rpc, DRENR, val); ++ ++ return 0; ++} ++ ++static int rpc_setup_writemode_nbits(struct rpc_spi *rpc, int cnb, ++ int anb, int dnb) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDB_MASK | SMENR_ADB_MASK | SMENR_SPIDB_MASK); ++ val |= SMENR_CDB(NBITS_TO_VAL(cnb)) ++ | SMENR_ADB(NBITS_TO_VAL(anb)) ++ | SMENR_SPIDB(NBITS_TO_VAL(dnb)); ++ rpc_write(rpc, SMENR, val); ++ ++ return 0; ++} ++ ++static void rpc_setup_write_mode_command_and_adr(struct rpc_spi *rpc, ++ int adr_width, bool ena) ++{ ++ u32 val; ++ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDB_MASK | SMENR_CDE | SMENR_ADE_MASK); ++ ++ if (ena) { ++ /* enable 1bit command */ ++ val |= SMENR_CDB_1B | SMENR_CDE; ++ ++ if (adr_width == 4) ++ val |= SMENR_ADE_31_0; ++ else ++ val |= SMENR_ADE_23_0; ++ } ++ rpc_write(rpc, SMENR, val); ++} ++ ++static int rpc_setup_write_mode(struct rpc_spi *rpc) ++{ ++ u32 val; ++ ++ rpc_wait(rpc, DEFAULT_TO); ++ ++ rpc_endisable_write_buf(rpc, true); ++ ++ /* ...setup manual mode */ ++ val = rpc_read(rpc, CMNCR); ++ val |= CMNCR_MD; ++ rpc_write(rpc, CMNCR, val); ++ ++ /* disable ddr */ ++ val = rpc_read(rpc, SMDRENR); ++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE); ++ rpc_write(rpc, SMDRENR, val); ++ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_OCDB_MASK | SMENR_DME | SMENR_OCDE | SMENR_SPIDB_MASK ++ | SMENR_ADB_MASK | SMENR_OPDE_MASK | SMENR_SPIDE_MASK); ++ val |= SMENR_SPIDE_32B; ++ rpc_write(rpc, SMENR, val); ++ ++ return 0; ++} ++ ++static void rpc_read_manual_data(struct rpc_spi *rpc, u32 *pv0, u32 *pv1) ++{ ++ u32 val0, val1, rd0, rd1; ++ ++ val0 = rpc_read(rpc, SMRDR0); ++ val1 = rpc_read(rpc, SMRDR1); ++ ++ if (rpc->mtdtype == MTD_QSPI_2x) { ++ rd1 = (val0 & 0xff000000) | ((val0 << 8) & 0xff0000) | ++ ((val1 >> 16) & 0xff00) | ((val1 >> 8) & 0xff); ++ rd0 = ((val0 & 0xff0000) << 8) | ((val0 << 16) & 0xff0000) | ++ ((val1 >> 8) & 0xff00) | (val1 & 0xff); ++ } else ++ rd0 = val0; ++ ++ if (pv0) ++ *pv0 = rd0; ++ ++ if (pv1 && rpc->mtdtype == MTD_QSPI_2x) ++ *pv1 = rd1; ++} ++ ++ ++static int rpc_datalen2trancfersize(struct rpc_spi *rpc, int len, bool copy) ++{ ++ int sz = len; ++ ++ if (len >= 2) ++ sz = 2; ++ ++ if (len >= 4) ++ sz = 4; ++ ++ if (rpc->mtdtype == MTD_QSPI_2x ++ && len >= 8 && !copy) ++ sz = 8; ++ ++ return sz; ++} ++ ++static int __rpc_write_data2reg(struct rpc_spi *rpc, int off, ++ const u8 *buf, int sz) ++{ ++ const u32 *b32 = (const u32 *)buf; ++ const u16 *b16 = (const u16 *)buf; ++ ++ if (sz == 4) ++ rpc_write(rpc, off, *b32); ++ else if (sz == 2) ++ writew(*b16, rpc->base+off); ++ else if (sz == 1) ++ writeb(*buf, rpc->base+off); ++ else if (sz != 0) { ++ dev_err(&rpc->pdev->dev, "incorrect data size %d\n", sz); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#define __SETVAL(x) ((((x) & 0xff) << 8) | ((x) & 0xff)) ++static int rpc_write_data2reg(struct rpc_spi *rpc, const u8 *buf, ++ int sz, bool copy) ++{ ++ int i, ret; ++ u32 v = 0; ++ ++ if (rpc->mtdtype == MTD_QSPI_2x) { ++ if (copy) { ++ for (i = 0; i < sz && i < 2; i++) ++ v |= (__SETVAL(buf[i]) << 16*i); ++ ++ ret = __rpc_write_data2reg(rpc, ++ sz == 4 ? SMWDR1 : SMWDR0, ++ (u8 *)&v, ++ sz == 4 ? sz : sz * 2); ++ if (ret) ++ return ret; ++ ++ v = 0; ++ for (; i < sz; i++) ++ v |= (__SETVAL(buf[i]) << 16*i); ++ ++ ++ ret = __rpc_write_data2reg(rpc, ++ sz == 4 ? SMWDR0 : SMWDR1, ++ (u8 *)&v, ++ sz == 4 ? sz : sz * 2); ++ if (ret) ++ return ret; ++ ++ return 0; ++ } ++ ++ sz >>= 1; ++ ret = __rpc_write_data2reg(rpc, ++ sz == 4 ? SMWDR1 : SMWDR0, ++ buf, ++ sz == 4 ? sz : sz * 2); ++ if (ret) ++ return ret; ++ buf += sz; ++ ++ return __rpc_write_data2reg(rpc, ++ sz == 4 ? SMWDR0 : SMWDR1, ++ buf, sz == 4 ? sz : sz * 2); ++ } ++ ++ return __rpc_write_data2reg(rpc, SMWDR0, buf, sz); ++} ++ ++static ssize_t rpc_write_unaligned(struct spi_nor *nor, loff_t to, size_t len, ++ const u_char *buf, size_t fullen) ++{ ++ int ret = len, dsize; ++ struct rpc_spi *rpc = nor->priv; ++ bool copy = false, last; ++ loff_t _to; ++ ++ rpc_endisable_write_buf(rpc, false); ++ ++ while (len > 0) { ++ _to = to; ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ _to >>= 1; ++ rpc_write(rpc, SMADR, _to); ++ dsize = rpc_datalen2trancfersize(rpc, len, copy); ++ ++ if (rpc_setup_data_size(rpc, dsize, copy)) ++ return -EINVAL; ++ ++ rpc_write_data2reg(rpc, buf, dsize, copy); ++ ++ last = (len <= dsize && fullen <= ret); ++ rpc_begin(rpc, false, true, last); ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ /* ...disable command */ ++ rpc_setup_write_mode_command_and_adr(rpc, ++ nor->addr_width, false); ++ ++ buf += dsize; ++ len -= dsize; ++ to += dsize; ++ } ++ ++ return ret; ++} ++ ++ ++ ++ ++static ssize_t rpc_write_flash(struct spi_nor *nor, loff_t to, size_t len, ++ const u_char *buf) ++{ ++ ssize_t res = len, full = len; ++ u32 val; ++ u8 rval[2]; ++ struct rpc_spi *rpc = nor->priv; ++ loff_t bo; ++ loff_t offset; ++ loff_t _to; ++ bool is_rounded = false; ++ ++ /* ...len should be rounded to 2 bytes */ ++ if (rpc->mtdtype == MTD_QSPI_2x && (len & 1)) { ++ is_rounded = true; ++ len &= ~(1); ++ } ++ ++ bo = to & (WRITE_BUF_ADR_MASK); ++ ++ rpc_flush_cache(rpc); ++ rpc_setup_write_mode(rpc); ++ rpc_setup_write_mode_command_and_adr(rpc, nor->addr_width, true); ++ rpc_setup_writemode_nbits(rpc, 1, 1, 1); ++ ++ /* ...setup command */ ++ val = rpc_read(rpc, SMCMR); ++ val &= ~(SMCMR_CMD_MASK); ++ val |= SMCMR_CMD(nor->program_opcode); ++ rpc_write(rpc, SMCMR, val); ++ ++ offset = (to & (~WRITE_BUF_ADR_MASK)); ++ ++ /* ...write unaligned first bytes */ ++ if (bo) { ++ rpc_write_unaligned(nor, to, WRITE_BUF_SIZE - bo, buf, full); ++ rpc_setup_write_mode(rpc); ++ ++ len -= WRITE_BUF_SIZE - bo; ++ buf += WRITE_BUF_SIZE - bo; ++ to += WRITE_BUF_SIZE - bo; ++ full -= WRITE_BUF_SIZE - bo; ++ } ++ ++ /* Unfortunatly RPC does not write properly in write buf mode ++ * without transferring command. ++ * May be it is better just remove this code ++ */ ++ if (len >= WRITE_BUF_SIZE && !bo) { ++ _to = to; ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ _to >>= 1; ++ rpc_write(rpc, SMADR, _to); ++ memcpy_toio(rpc->write_area, buf, WRITE_BUF_SIZE); ++ buf += WRITE_BUF_SIZE; ++ len -= WRITE_BUF_SIZE; ++ to += WRITE_BUF_SIZE; ++ full -= WRITE_BUF_SIZE; ++ ++ rpc_begin(rpc, false, true, full <= 0); ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ rpc_setup_write_mode_command_and_adr(rpc, ++ nor->addr_width, false); ++ } ++ ++ if (len) { ++ rpc_write_unaligned(nor, to, len, buf, full); ++ buf += len; ++ to += len; ++ full -= len; ++ len = 0; ++ } ++ ++ if (is_rounded) { ++ rval[0] = *buf; ++ rval[1] = 0xFF; ++ rpc_write_unaligned(nor, to, 2, rval, full); ++ } ++ ++ rpc_flush_cache(rpc); ++ ++ return res; ++} ++ ++static inline unsigned int rpc_rx_nbits(struct spi_nor *nor) ++{ ++ switch (nor->flash_read) { ++ case SPI_NOR_DUAL: ++ return 2; ++ case SPI_NOR_QUAD: ++ return 4; ++ default: ++ return 0; ++ } ++} ++ ++ ++#if 0 ++//manual read ++static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len, ++ u_char *buf) ++{ ++ ssize_t ret = len; ++ struct rpc_spi *rpc = nor->priv; ++ int adr_width = nor->addr_width; ++ int opcode_nbits = 1;//SPI_NBITS_SINGLE; ++ int addr_nbits = spi_nor_get_read_addr_nbits(nor->read_opcode); ++ int data_nbits = rpc_rx_nbits(nor); ++ int dummy = nor->read_dummy - 1; ++ u32 val, val2; ++ u32 *buf32; ++ int sz = 4; ++ int i, j; ++ loff_t adr = from; ++ ++ rpc_wait(rpc, DEFAULT_TO); ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ sz <<= 1; ++ ++ /* ...setup manual mode */ ++ val = rpc_read(rpc, CMNCR); ++ val |= CMNCR_MD; ++ rpc_write(rpc, CMNCR, val); ++ ++ /*...setup dummy */ ++ val = rpc_read(rpc, SMDMCR); ++ val &= ~(SMDMCR_DMCYC_MASK); ++ val |= SMDMCR_DMCYC(dummy); ++ rpc_write(rpc, SMDMCR, val); ++ ++ /* disable ddr */ ++ val = rpc_read(rpc, SMDRENR); ++ val &= ~(SMDRENR_ADDRE | SMDRENR_OPDRE | SMDRENR_SPIDRE); ++ rpc_write(rpc, SMDRENR, val); ++ ++ /* enable 1bit command */ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDB_MASK | SMENR_OCDB_MASK | SMENR_DME ++ | SMENR_OCDE | SMENR_SPIDB_MASK ++ | SMENR_ADE_MASK | SMENR_ADB_MASK ++ | SMENR_OPDE_MASK | SMENR_SPIDE_MASK); ++ val |= SMENR_CDB_1B | SMENR_ADB_4B | SMENR_SPIDB_4B ++ | SMENR_CDE | SMENR_ADE_31_0 | SMENR_DME | SMENR_SPIDE_32B; ++ rpc_write(rpc, SMENR, val); ++ ++ /* ...setup command */ ++ val = rpc_read(rpc, SMCMR); ++ val &= ~(SMCMR_CMD_MASK); ++ val |= SMCMR_CMD(nor->read_opcode); ++ rpc_write(rpc, SMCMR, val); ++ ++ buf32 = (u32 *)buf; ++ ++ while (len > 0) { ++ adr = from + ret - len; ++ ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ adr >>= 1; ++ ++ rpc_write(rpc, SMADR, adr); ++ ++ rpc_begin(rpc, true, false, len <= sz); ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ rpc_read_manual_data(rpc, &val, &val2); ++ ++ j = 0; ++ do { ++ if (len > 4) { ++ *buf32 = val; ++ buf32++; ++ len -= 4; ++ } else { ++ buf = (u8 *)buf32; ++ for (i = 0; i < len; i++) { ++ *buf = (val >> (8 * i)) & 0x000000ff; ++ buf++; ++ } ++ len = 0; ++ } ++ val = val2; ++ j++; ++ } while (j < 2 && rpc->mtdtype == MTD_QSPI_2x); ++ } ++ ++ return ret; ++} ++#else ++#define READ_ADR_MASK (BIT(26) - 1) ++static ssize_t rpc_read_flash(struct spi_nor *nor, loff_t from, size_t len, ++ u_char *buf) ++{ ++ u32 val; ++ struct rpc_spi *rpc = nor->priv; ++ int adr_width = nor->addr_width; ++ int opcode_nbits = 1; ++ int addr_nbits = spi_nor_get_read_addr_nbits(nor->read_opcode); ++ int data_nbits = rpc_rx_nbits(nor); ++ int dummy = nor->read_dummy - 1; ++ ssize_t ret = len; ++ ssize_t readlen; ++ loff_t _from; ++ ++ rpc_setup_ext_mode(rpc); ++ /* ...setup n bits */ ++ rpc_setup_extmode_nbits(rpc, opcode_nbits, addr_nbits, data_nbits); ++ ++ /* TODO: setup DDR */ ++ ++ /* ...setup command */ ++ val = rpc_read(rpc, DRCMR); ++ val &= ~(DRCMR_CMD_MASK); ++ val |= DRCMR_CMD(nor->read_opcode); ++ rpc_write(rpc, DRCMR, val); ++ ++ /* ...setup dummy cycles */ ++ val = rpc_read(rpc, DRDMCR); ++ val &= ~(DRDMCR_DMCYC_MASK); ++ val |= DRDMCR_DMCYC(dummy); ++ rpc_write(rpc, DRDMCR, val); ++ ++ /* ...setup read sequence */ ++ val = rpc_read(rpc, DRENR); ++ val |= DRENR_DME | DRENR_CDE; ++ rpc_write(rpc, DRENR, val); ++ ++ while (len > 0) { ++ /* ...setup address */ ++ rpc_setup_extmode_read_addr(rpc, adr_width, from); ++ /* ...use adr [25...0] */ ++ _from = from & READ_ADR_MASK; ++ ++ readlen = READ_ADR_MASK - _from + 1; ++ readlen = readlen > len ? len : readlen; ++ ++ memcpy_fromio(buf, rpc->read_area + _from, readlen); ++ buf += readlen; ++ from += readlen; ++ len -= readlen; ++ } ++ ++ return ret; ++} ++#endif ++ ++static int __rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) ++{ ++ u32 val, val2; ++ u32 *buf32; ++ int i; ++ u32 mask = 0, type; ++ struct rpc_spi *rpc = nor->priv; ++ ++ type = rpc->mtdtype; ++ ++ rpc_setup_reg_mode(rpc); ++ val = rpc_read(rpc, SMCMR); ++ val &= ~(SMCMR_CMD_MASK); ++ val |= SMCMR_CMD(opcode); ++ rpc_write(rpc, SMCMR, val); ++ ++ rpc_begin(rpc, true, false, len <= 4); ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ /* ...disable command */ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDE); ++ rpc_write(rpc, SMENR, val); ++ ++ buf32 = (u32 *)buf; ++ ++ while (len > 0) { ++ rpc_read_manual_data(rpc, &val, &val2); ++ ++ if (mask) { ++ dev_warn(&rpc->pdev->dev, ++ "Using mask workaround (0x%x)\n", mask); ++ val &= ~(mask); ++ val2 &= ~(mask); ++ } ++ ++ /* ... spi flashes should be the same */ ++ if (type == MTD_QSPI_2x && val != val2) { ++ /* clear cs */ ++ rpc_begin(rpc, true, false, true); ++ return -EAGAIN; ++ } ++ ++ if (len > 4) { ++ *buf32 = val; ++ buf32++; ++ len -= 4; ++ } else { ++ buf = (u8 *)buf32; ++ for (i = 0; i < len; i++) { ++ *buf = (val >> (8 * i)) & 0x000000ff; ++ buf++; ++ } ++ len = 0; ++ } ++ ++ if (!len) ++ break; ++ ++ mask = 0xff; ++ ++ rpc_begin(rpc, true, false, len <= 4); ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ } ++ ++ return 0; ++} ++ ++ ++static int rpc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) ++{ ++ int i, ret; ++ ++ /* A few read commands like read status can ++ * generate different answers. We repeat reading ++ * in that case ++ */ ++ for (i = 0; i < REPEAT_MAX; i++) { ++ ret = __rpc_read_reg(nor, opcode, buf, len); ++ if (!ret || ret != -EAGAIN) ++ break; ++ mdelay(REPEAT_TIME); ++ } ++ ++ return ret; ++} ++ ++ ++ ++static int rpc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) ++{ ++ struct rpc_spi *rpc = nor->priv; ++ u32 val; ++ int dsize; ++ bool copy = true; ++ ++ rpc_setup_reg_mode(rpc); ++ ++ val = rpc_read(rpc, SMCMR); ++ val &= ~(SMCMR_CMD_MASK); ++ val |= SMCMR_CMD(opcode); ++ rpc_write(rpc, SMCMR, val); ++ ++ dsize = rpc_datalen2trancfersize(rpc, len, copy); ++ ++ if (rpc_setup_data_size(rpc, dsize, copy)) ++ return -EINVAL; ++ ++ if (rpc_write_data2reg(rpc, buf, dsize, copy)) ++ return -EINVAL; ++ buf += dsize; ++ len -= dsize; ++ rpc_begin(rpc, false, dsize > 0, len == 0); ++ ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ /* ...disable command */ ++ val = rpc_read(rpc, SMENR); ++ val &= ~(SMENR_CDE); ++ rpc_write(rpc, SMENR, val); ++ ++ while (len > 0) { ++ dsize = rpc_datalen2trancfersize(rpc, len, copy); ++ if (rpc_setup_data_size(rpc, dsize, copy)) ++ return -EINVAL; ++ rpc_write_data2reg(rpc, buf, dsize, copy); ++ buf += dsize; ++ len -= dsize; ++ ++ rpc_begin(rpc, false, dsize, len == 0); ++ ++ if (rpc_wait(rpc, DEFAULT_TO)) ++ return -ETIMEDOUT; ++ ++ } ++ ++ return 0; ++} ++ ++ ++ ++/* hw init for spi-nor flashes */ ++static int rpc_hw_init_1x2x(struct rpc_spi *rpc) ++{ ++ u32 val; ++ ++ /* Exec calibration */ ++ val = rpc_read(rpc, PHYCNT); ++ val &= ~(PHYCNT_OCTA_MASK | PHYCNT_EXDS | PHYCNT_OCT ++ | PHYCNT_DDRCAL | PHYCNT_HS | PHYCNT_STREAM_MASK ++ | PHYCNT_WBUF2 | PHYCNT_WBUF | PHYCNT_PHYMEM_MASK); ++ val |= (PHYCNT_CAL) | PHYCNT_STREAM(6); ++ rpc_write(rpc, PHYCNT, val); ++ ++ /* disable rpc_* pins */ ++ val = rpc_read(rpc, PHYINT); ++ val &= ~((1<<24) | (7<<16)); ++ rpc_write(rpc, PHYINT, val); ++ ++ val = rpc_read(rpc, SMDRENR); ++ val &= ~(SMDRENR_HYPE_MASK); ++ val |= SMDRENR_HYPE_SPI_FLASH; ++ rpc_write(rpc, SMDRENR, val); ++ ++ val = rpc_read(rpc, CMNCR); ++ val &= ~(CMNCR_BSZ_MASK); ++ if (rpc->mtdtype != MTD_QSPI_1x) ++ val |= CMNCR_BSZ_4x2; ++ rpc_write(rpc, CMNCR, val); ++ ++ val = rpc_read(rpc, PHYOFFSET1); ++ val |= PHYOFFSET1_DDRTMG; ++ rpc_write(rpc, PHYOFFSET1, val); ++ ++ val = SSLDR_SPNDL(0) | SSLDR_SLNDL(4) | SSLDR_SCKDL(0); ++ rpc_write(rpc, SSLDR, val); ++ ++ return 0; ++} ++ ++ ++static int rpc_hw_init(struct rpc_spi *rpc) ++{ ++ switch (rpc->mtdtype) { ++ case MTD_QSPI_1x: ++ case MTD_QSPI_2x: ++ return rpc_hw_init_1x2x(rpc); ++ ++ default: ++ dev_err(&rpc->pdev->dev, "Unsupported connection mode\n"); ++ return -ENODEV; ++ } ++} ++ ++ ++static int rpc_erase_sector(struct spi_nor *nor, loff_t addr) ++{ ++ struct rpc_spi *rpc = nor->priv; ++ u8 buf[6]; ++ int i; ++ ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ addr >>= 1; ++ ++ for (i = nor->addr_width - 1; i >= 0; i--) { ++ buf[i] = addr & 0xff; ++ addr >>= 8; ++ } ++ ++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); ++} ++ ++ ++ ++ ++static const struct of_device_id rpc_of_match[] = { ++ { .compatible = "renesas,qspi-rpc-r8a7798" }, ++ { .compatible = "renesas,qspi-rpc-r8a7797", .data = (void *)OWN_CLOCK_DIVIDER }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, rpc_of_match); ++ ++ ++ ++static int rpc_spi_probe(struct platform_device *pdev) ++{ ++ struct device_node *flash_np; ++ struct spi_nor *nor; ++ struct rpc_spi *rpc; ++ struct resource *res; ++ enum read_mode mode = SPI_NOR_NORMAL; ++ u32 max_clk_rate = 50000000; ++ u32 property; ++ int ret; ++ int own_clk; ++ ++ ++ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL); ++ if (!flash_np) { ++ dev_err(&pdev->dev, "no SPI flash device to configure\n"); ++ return -ENODEV; ++ } ++ ++ if (!of_property_read_u32(flash_np, "spi-rx-bus-width", &property)) { ++ switch (property) { ++ case 1: ++ break; ++ case 2: ++ mode = SPI_NOR_DUAL; ++ break; ++ case 4: ++ mode = SPI_NOR_QUAD; ++ break; ++ default: ++ dev_err(&pdev->dev, "unsupported rx-bus-width\n"); ++ return -EINVAL; ++ } ++ } ++ ++ of_property_read_u32(flash_np, "spi-max-frequency", &max_clk_rate); ++ own_clk = of_device_get_match_data(&pdev->dev); ++ ++ rpc = devm_kzalloc(&pdev->dev, sizeof(*rpc), GFP_KERNEL); ++ if (!rpc) ++ return -ENOMEM; ++ ++ rpc->pdev = pdev; ++ ++ /* ... setup nor hooks */ ++ nor = &rpc->spi_nor; ++ nor->dev = &pdev->dev; ++ spi_nor_set_flash_node(nor, flash_np); ++ nor->read = rpc_read_flash; ++ nor->write = rpc_write_flash; ++ nor->read_reg = rpc_read_reg; ++ nor->write_reg = rpc_write_reg; ++ nor->priv = rpc; ++ rpc->mtdtype = MTD_QSPI_1x; ++ if (of_find_property(pdev->dev.of_node, "dual", NULL)) { ++ rpc->mtdtype = MTD_QSPI_2x; ++ spi_nor_set_array_size(nor, 2); ++ } ++ if (rpc->mtdtype == MTD_QSPI_2x) ++ nor->erase = rpc_erase_sector; ++ ++ /* ...get memory */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ rpc->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(rpc->base)) { ++ dev_err(&pdev->dev, "cannot get resources\n"); ++ ret = PTR_ERR(rpc->base); ++ goto error; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ ++ rpc->read_area = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(rpc->base)) { ++ dev_err(&pdev->dev, "cannot get resources\n"); ++ ret = PTR_ERR(rpc->base); ++ goto error; ++ } ++ ++ /* ...get memory */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ rpc->write_area = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(rpc->base)) { ++ dev_err(&pdev->dev, "cannot get resources\n"); ++ ret = PTR_ERR(rpc->base); ++ goto error; ++ } ++ ++ /* ...get clk */ ++ rpc->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rpc->clk)) { ++ dev_err(&pdev->dev, "cannot get clock\n"); ++ ret = PTR_ERR(rpc->clk); ++ goto error; ++ } ++ ++ /* ...set max clk rate */ ++ if (!own_clk) { ++ ret = clk_set_rate(rpc->clk, max_clk_rate); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot set clock rate\n"); ++ goto error; ++ } ++ } ++ ++ /* ... enable clk */ ++ ret = clk_prepare_enable(rpc->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot prepare clock\n"); ++ goto error; ++ } ++ ++ /* ...init device */ ++ ret = rpc_hw_init(rpc); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "rpc_hw_init error.\n"); ++ goto error_clk_disable; ++ } ++ ++ /* ...set clk ratio */ ++ if (own_clk) { ++ ret = rpc_setup_clk_ratio(rpc, max_clk_rate); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot set clock ratio\n"); ++ goto error; ++ } ++ } ++ ++ platform_set_drvdata(pdev, rpc); ++ ++ ret = spi_nor_scan(nor, NULL, mode); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_nor_scan error.\n"); ++ goto error_clk_disable; ++ } ++ ++ ret = mtd_device_register(&nor->mtd, NULL, 0); ++ if (ret) { ++ dev_err(&pdev->dev, "mtd_device_register error.\n"); ++ goto error_clk_disable; ++ } ++ ++ dev_info(&pdev->dev, "probed as %s\n", ++ rpc->mtdtype == MTD_QSPI_1x ? "single" : "dual"); ++ ++ return 0; ++ ++ ++error_clk_disable: ++ clk_disable_unprepare(rpc->clk); ++error: ++ return ret; ++} ++ ++ ++ ++ ++static int rpc_spi_remove(struct platform_device *pdev) ++{ ++ struct rpc_spi *rpc = platform_get_drvdata(pdev); ++ ++ /* HW shutdown */ ++ clk_disable_unprepare(rpc->clk); ++ mtd_device_unregister(&rpc->spi_nor.mtd); ++ return 0; ++} ++ ++ ++ ++ ++ ++/* platform driver interface */ ++static struct platform_driver rpc_platform_driver = { ++ .probe = rpc_spi_probe, ++ .remove = rpc_spi_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "rpc", ++ .of_match_table = of_match_ptr(rpc_of_match), ++ }, ++}; ++ ++module_platform_driver(rpc_platform_driver); ++ ++ ++MODULE_ALIAS("rpc"); ++MODULE_AUTHOR("Cogent Embedded Inc. "); ++MODULE_DESCRIPTION("Renesas RPC Driver"); ++MODULE_LICENSE("GPL"); +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch new file mode 100644 index 0000000..7169baa --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch @@ -0,0 +1,35 @@ +From 80ebc1949eb5d7db33c60faa8537029fcf6379ba Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 11:14:26 +0300 +Subject: [PATCH 05/12] R8A7798: dtsi: Add RPC node to dtsi + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7798.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7798.dtsi b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +index e2b3404..fac60e6 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7798.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +@@ -1026,6 +1026,17 @@ + status = "disabled"; + }; + ++ qspi0: qspi@ee200000 { ++ compatible = "renesas,qspi-rpc-r8a7798"; ++ reg = <0 0xee200000 0 0x1f0>, ++ <0 0x08000000 0 0x04000000>, ++ <0 0xee208000 0 0x100>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 917>; ++ power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ status = "disabled"; ++ }; ++ + msiof0: spi@e6e90000 { + #address-cells = <1>; + #size-cells = <0>; +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch new file mode 100644 index 0000000..213421c --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0115-R8A7798-condor-dts-Add-qspi-flash.patch @@ -0,0 +1,98 @@ +From 45ac82977a015e6ec80a1fb119b26c5b5841b760 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 11:19:54 +0300 +Subject: [PATCH 06/12] R8A7798-condor: dts: Add qspi flash + +Add s25fs512 flash support + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7798-condor.dts | 72 ++++++++++++++++++++++++++ + 1 file changed, 72 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7798-condor.dts b/arch/arm64/boot/dts/renesas/r8a7798-condor.dts +index cdd9844..81f984f1 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7798-condor.dts ++++ b/arch/arm64/boot/dts/renesas/r8a7798-condor.dts +@@ -240,6 +240,78 @@ + groups = "tpu_to0"; + function = "tpu"; + }; ++ ++ qspi0_pins: qspi0 { ++ groups = "qspi0_ctrl", "qspi0_data4"; ++ function = "qspi0"; ++ }; ++ ++ qspi1_pins: qspi1 { ++ groups = "qspi1_ctrl", "qspi1_data4"; ++ function = "qspi1"; ++ }; ++}; ++ ++&qspi0 { ++ pinctrl-0 = <&qspi0_pins &qspi1_pins>; ++ pinctrl-names = "default"; ++ ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "spansion,s25fs512s", "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-rx-bus-width = <4>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ bootparam@0 { ++ reg = <0x00000000 0x040000>; ++ read-only; ++ }; ++ cr7@00040000 { ++ reg = <0x00040000 0x080000>; ++ read-only; ++ }; ++ cert_header_sa3@000C0000 { ++ reg = <0x000C0000 0x080000>; ++ read-only; ++ }; ++ bl2@00140000 { ++ reg = <0x00140000 0x040000>; ++ read-only; ++ }; ++ cert_header_sa6@00180000 { ++ reg = <0x00180000 0x040000>; ++ read-only; ++ }; ++ bl31@001C0000 { ++ reg = <0x001C0000 0x460000>; ++ read-only; ++ }; ++ uboot@00640000 { ++ reg = <0x00640000 0x0C0000>; ++ read-only; ++ }; ++ uboot-env@00700000 { ++ reg = <0x00700000 0x040000>; ++ read-only; ++ }; ++ dtb@00740000 { ++ reg = <0x00740000 0x080000>; ++ }; ++ kernel@007C0000 { ++ reg = <0x007C0000 0x1400000>; ++ }; ++ user@01BC0000 { ++ reg = <0x01BC0000 0x2440000>; ++ }; ++ }; ++ }; + }; + + &scif0 { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch new file mode 100644 index 0000000..7a3500b --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0116-spi-nor-Add-s25fs512-flash-support.patch @@ -0,0 +1,126 @@ +From 1b2a145d72c4202fc41b2af138b862a8be94b0f0 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 11:29:21 +0300 +Subject: [PATCH 07/12] spi-nor: Add s25fs512 flash support + +Signed-off-by: Dmitry Shifrin +--- + drivers/mtd/spi-nor/spi-nor.c | 31 ++++++++++++++++++++++++++++--- + include/linux/mtd/spi-nor.h | 15 +++++++++++++++ + 2 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index d0fc165..1855bbe 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -75,6 +75,7 @@ struct flash_info { + * bit. Must be used with + * SPI_NOR_HAS_LOCK. + */ ++#define SPANSION_S512FS_QUIRK BIT(10) + }; + + #define JEDEC_MFR(info) ((info)->id[0]) +@@ -222,8 +223,18 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor) + int sr = read_sr(nor); + if (sr < 0) + return sr; +- else +- return !(sr & SR_WIP); ++ ++ if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) { ++ if (sr & SR_E_ERR) ++ dev_err(nor->dev, "Erase Error occurred\n"); ++ else ++ dev_err(nor->dev, "Programming Error occurred\n"); ++ ++ nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); ++ return -EIO; ++ } ++ ++ return !(sr & SR_WIP); + } + + static inline int spi_nor_fsr_ready(struct spi_nor *nor) +@@ -901,7 +912,21 @@ static const struct flash_info spi_nor_ids[] = { + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ ++ { ++ "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK) ++ }, ++ { ++ "s25fs128s0", INFO6(0x012018, 0x4d0381, 256 * 1024, 64, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK) ++ }, ++ { ++ "s25fs128s1", INFO6(0x012018, 0x4d0181, 64 * 1024, 256, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPANSION_S512FS_QUIRK) ++ }, ++ + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index c425c7b..99f7b2a 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -60,8 +60,10 @@ + #define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ + #define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ + #define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ ++#define SPINOR_OP_READ4_1_4_4 0xec /* Read data bytes (Quad I/O SPI) */ + #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ + #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ ++#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */ + + /* Used for SST flashes only. */ + #define SPINOR_OP_BP 0x02 /* Byte program */ +@@ -74,6 +76,7 @@ + + /* Used for Spansion flashes only. */ + #define SPINOR_OP_BRWR 0x17 /* Bank register write */ ++#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ + + /* Used for Micron flashes only. */ + #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ +@@ -88,6 +91,9 @@ + #define SR_BP2 BIT(4) /* Block protect 2 */ + #define SR_TB BIT(5) /* Top/Bottom protect */ + #define SR_SRWD BIT(7) /* SR write protect */ ++/* Spansion/Cypress specific status bits */ ++#define SR_E_ERR BIT(5) ++#define SR_P_ERR BIT(6) + + #define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ + +@@ -119,6 +125,7 @@ enum spi_nor_ops { + enum spi_nor_option_flags { + SNOR_F_USE_FSR = BIT(0), + SNOR_F_HAS_SR_TB = BIT(1), ++ SNOR_F_USE_CLSR = BIT(5), + }; + + /** +@@ -197,6 +204,14 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) + return mtd_get_of_node(&nor->mtd); + } + ++static inline int spi_nor_get_read_addr_nbits(u8 opcode) ++{ ++ if (opcode == SPINOR_OP_READ4_1_4_4) ++ return 4; ++ return 1; ++} ++ ++ + /** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch new file mode 100644 index 0000000..5003ae6 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0117-spi-nor-Add-flash-array-support.patch @@ -0,0 +1,89 @@ +From 0d10eb085d28a442b147e63383d1836cb3e62ae4 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Mon, 19 Mar 2018 11:32:58 +0300 +Subject: [PATCH 08/12] spi-nor: Add flash array support + +Signed-off-by: Dmitry Shifrin +--- + drivers/mtd/spi-nor/spi-nor.c | 20 ++++++++++++++++++-- + include/linux/mtd/spi-nor.h | 12 ++++++++++++ + 2 files changed, 30 insertions(+), 2 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 1855bbe..3ced558 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1338,6 +1338,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (ret) + return ret; + ++ if (!nor->array_size) ++ nor->array_size = 1; ++ + if (name) + info = spi_nor_match_id(name); + /* Try to auto-detect if chip name wasn't specified or not found */ +@@ -1500,7 +1503,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: +- nor->read_opcode = SPINOR_OP_READ4_1_1_4; ++ if (info->flags & SPANSION_S512FS_QUIRK) ++ nor->read_opcode = SPINOR_OP_READ4_1_4_4; ++ else ++ nor->read_opcode = SPINOR_OP_READ4_1_1_4; + break; + case SPI_NOR_DUAL: + nor->read_opcode = SPINOR_OP_READ4_1_1_2; +@@ -1528,7 +1534,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + return -EINVAL; + } + +- nor->read_dummy = spi_nor_read_dummy_cycles(nor); ++ if (info->flags & SPANSION_S512FS_QUIRK) { ++ nor->read_dummy = 10; ++ nor->flags |= SNOR_F_USE_CLSR; ++ } else ++ nor->read_dummy = spi_nor_read_dummy_cycles(nor); ++ ++ /* recount parameters using array size */ ++ mtd->erasesize *= nor->array_size; ++ mtd->size *= nor->array_size; ++ nor->page_size *= nor->array_size; ++ mtd->writebufsize *= nor->array_size; + + dev_info(dev, "%s (%lld Kbytes)\n", info->name, + (long long)mtd->size >> 10); +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 99f7b2a..7216b09 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -174,6 +174,7 @@ struct spi_nor { + bool sst_write_second; + u32 flags; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; ++ u8 array_size; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); +@@ -204,6 +205,17 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) + return mtd_get_of_node(&nor->mtd); + } + ++static inline void spi_nor_set_array_size(struct spi_nor *nor, ++ u8 size) ++{ ++ nor->array_size = size; ++} ++ ++static inline u8 spi_nor_get_array_size(struct spi_nor *nor) ++{ ++ return nor->array_size; ++} ++ + static inline int spi_nor_get_read_addr_nbits(u8 opcode) + { + if (opcode == SPINOR_OP_READ4_1_4_4) +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch new file mode 100644 index 0000000..7910790 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0118-r8a7797-clk-Add-rpc-clock.patch @@ -0,0 +1,45 @@ +From 730f3d906fa599bf76bb0b1fdf35b44a7cbdc2ed Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 22 Mar 2018 14:57:46 +0300 +Subject: [PATCH 09/12] r8a7797: clk: Add rpc clock + +Signed-off-by: Dmitry Shifrin +--- + drivers/clk/renesas/r8a7797-cpg-mssr.c | 2 ++ + include/dt-bindings/clock/r8a7797-cpg-mssr.h | 1 + + 2 files changed, 3 insertions(+) + +diff --git a/drivers/clk/renesas/r8a7797-cpg-mssr.c b/drivers/clk/renesas/r8a7797-cpg-mssr.c +index a3eed41..9763f6c 100644 +--- a/drivers/clk/renesas/r8a7797-cpg-mssr.c ++++ b/drivers/clk/renesas/r8a7797-cpg-mssr.c +@@ -74,6 +74,7 @@ static const struct cpg_core_clk r8a7797_core_clks[] __initconst = { + DEF_FIXED("s2d1", R8A7797_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A7797_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A7797_CLK_S2D4, CLK_S2, 4, 1), ++ DEF_FIXED("rpcsrc", R8A7797_CLK_RPCSRC, CLK_PLL1_DIV2, 5, 1), + + DEF_GEN3_SD0H("sd0h", R8A7797_CLK_SD0H, CLK_PLL1_DIV4, 0x0074), + DEF_GEN3_SD0("sd0", R8A7797_CLK_SD0, CLK_PLL1_DIV4, 0x0074), +@@ -158,6 +159,7 @@ static const struct mssr_mod_clk r8a7797_mod_clks[] __initconst = { + DEF_MOD("i2c2", 929, R8A7797_CLK_S2D2), + DEF_MOD("i2c1", 930, R8A7797_CLK_S2D2), + DEF_MOD("i2c0", 931, R8A7797_CLK_S2D2), ++ DEF_MOD("rpc", 917, R8A7797_CLK_RPCSRC), + }; + + static const unsigned int r8a7797_crit_mod_clks[] __initconst = { +diff --git a/include/dt-bindings/clock/r8a7797-cpg-mssr.h b/include/dt-bindings/clock/r8a7797-cpg-mssr.h +index ae6b3af..c3bd0a7 100644 +--- a/include/dt-bindings/clock/r8a7797-cpg-mssr.h ++++ b/include/dt-bindings/clock/r8a7797-cpg-mssr.h +@@ -44,5 +44,6 @@ + #define R8A7797_CLK_CPEX 29 + #define R8A7797_CLK_R 30 + #define R8A7797_CLK_OSC 31 ++#define R8A7797_CLK_RPCSRC 32 + + #endif /* __DT_BINDINGS_CLOCK_R8A7797_CPG_MSSR_H__ */ +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch new file mode 100644 index 0000000..a0b4330 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0119-r8a7797-pinctrl-Add-qspi-pins.patch @@ -0,0 +1,118 @@ +From 441ae85fe3fec270ea1284a669cdd348a8432d85 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 22 Mar 2018 15:00:02 +0300 +Subject: [PATCH 10/12] r8a7797: pinctrl: Add qspi pins + +Signed-off-by: Dmitry Shifrin +--- + drivers/pinctrl/sh-pfc/pfc-r8a7797.c | 73 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c +index 6b83f44..4f451c4 100644 +--- a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c ++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c +@@ -1143,6 +1143,59 @@ static const unsigned int scif_clk_b_mux[] = { + SCIF_CLK_B_MARK, + }; + ++/* - QSPI ------------------------------------------------------------------- */ ++static const unsigned int qspi0_ctrl_pins[] = { ++ /* QSPI0_SPCLK QSPI0_SSL */ ++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 0), ++}; ++static const unsigned int qspi0_ctrl_mux[] = { ++ QSPI0_SPCLK_MARK, QSPI0_SSL_MARK, ++}; ++ ++static const unsigned int qspi0_data2_pins[] = { ++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1 */ ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2), ++}; ++static const unsigned int qspi0_data2_mux[] = { ++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK, ++}; ++ ++static const unsigned int qspi0_data4_pins[] = { ++ /* QSPI0_MOSI_IO0, QSPI0_MISO_IO1, QSPI0_IO2, QSPI0_IO3 */ ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2), ++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 4), ++}; ++static const unsigned int qspi0_data4_mux[] = { ++ QSPI0_MOSI_IO0_MARK, QSPI0_MISO_IO1_MARK, ++ QSPI0_IO2_MARK, QSPI0_IO3_MARK ++}; ++ ++static const unsigned int qspi1_ctrl_pins[] = { ++ /* QSPI1_SPCLK QSPI1_SSL */ ++ RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 11), ++}; ++static const unsigned int qspi1_ctrl_mux[] = { ++ QSPI1_SPCLK_MARK, QSPI1_SSL_MARK, ++}; ++ ++static const unsigned int qspi1_data2_pins[] = { ++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1 */ ++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8), ++}; ++static const unsigned int qspi1_data2_mux[] = { ++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK, ++}; ++ ++static const unsigned int qspi1_data4_pins[] = { ++ /* QSPI1_MOSI_IO0, QSPI1_MISO_IO1, QSPI1_IO2, QSPI1_IO3 */ ++ RCAR_GP_PIN(5, 7), RCAR_GP_PIN(5, 8), ++ RCAR_GP_PIN(5, 9), RCAR_GP_PIN(5, 10), ++}; ++static const unsigned int qspi1_data4_mux[] = { ++ QSPI1_MOSI_IO0_MARK, QSPI1_MISO_IO1_MARK, ++ QSPI1_IO2_MARK, QSPI1_IO3_MARK ++}; ++ + /* - I2C -------------------------------------------------------------------- */ + static const unsigned int i2c0_pins[] = { + /* SDA0, SCL0 */ +@@ -1971,6 +2024,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(vin1_field), + SH_PFC_PIN_GROUP(vin1_clkenb), + SH_PFC_PIN_GROUP(vin1_clk), ++ SH_PFC_PIN_GROUP(qspi0_ctrl), ++ SH_PFC_PIN_GROUP(qspi0_data2), ++ SH_PFC_PIN_GROUP(qspi0_data4), ++ SH_PFC_PIN_GROUP(qspi1_ctrl), ++ SH_PFC_PIN_GROUP(qspi1_data2), ++ SH_PFC_PIN_GROUP(qspi1_data4), + }; + + static const char * const avb0_groups[] = { +@@ -2201,6 +2260,18 @@ static const char * const vin1_groups[] = { + "vin1_clk", + }; + ++static const char * const qspi0_groups[] = { ++ "qspi0_ctrl", ++ "qspi0_data2", ++ "qspi0_data4", ++}; ++ ++static const char * const qspi1_groups[] = { ++ "qspi1_ctrl", ++ "qspi1_data2", ++ "qspi1_data4", ++}; ++ + #define POCCTRL0 0x380 + #define POCCTRL1 0x384 + #define PIN2POCCTRL0_SHIFT(a) ({ \ +@@ -2244,6 +2315,8 @@ static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(tmu), + SH_PFC_FUNCTION(vin0), + SH_PFC_FUNCTION(vin1), ++ SH_PFC_FUNCTION(qspi0), ++ SH_PFC_FUNCTION(qspi1), + }; + + static const struct pinmux_cfg_reg pinmux_config_regs[] = { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch new file mode 100644 index 0000000..8d782ca --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0120-r8a7797-dtsi-Add-rpc-node.patch @@ -0,0 +1,35 @@ +From 847b14e65374563ec3ee1d06eec05b4004233e7d Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 22 Mar 2018 15:27:49 +0300 +Subject: [PATCH 11/12] r8a7797: dtsi: Add rpc node + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7797.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7797.dtsi b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +index a5552d6..2beca53 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +@@ -889,6 +889,17 @@ + status = "disabled"; + }; + ++ qspi0: qspi@ee200000 { ++ compatible = "renesas,qspi-rpc-r8a7797"; ++ reg = <0 0xee200000 0 0x1f0>, ++ <0 0x08000000 0 0x04000000>, ++ <0 0xee208000 0 0x100>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 917>; ++ power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ status = "disabled"; ++ }; ++ + msiof0: spi@e6e90000 { + #address-cells = <1>; + #size-cells = <0>; +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch new file mode 100644 index 0000000..8dabedc --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0121-r8a7797-eagle-dts-Add-spi-flash-node.patch @@ -0,0 +1,96 @@ +From 0e884fcce89a6b15bd3971392b5641b48d1ad9ab Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 22 Mar 2018 15:31:48 +0300 +Subject: [PATCH 12/12] r8a7797-eagle: dts: Add spi flash node + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7797-eagle.dts | 72 +++++++++++++++++++++++++++ + 1 file changed, 72 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts b/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts +index ce7a88e..c8150d9 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts ++++ b/arch/arm64/boot/dts/renesas/r8a7797-eagle.dts +@@ -203,6 +203,78 @@ + groups = "du_rgb666", "du_sync", "du_clk_out_0", "du_disp"; + function = "du"; + }; ++ ++ qspi0_pins: qspi0 { ++ groups = "qspi0_ctrl", "qspi0_data4"; ++ function = "qspi0"; ++ }; ++ ++ qspi1_pins: qspi1 { ++ groups = "qspi1_ctrl", "qspi1_data4"; ++ function = "qspi1"; ++ }; ++}; ++ ++&qspi0 { ++ pinctrl-0 = <&qspi0_pins &qspi1_pins>; ++ pinctrl-names = "default"; ++ ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "spansion,s25fs512s", "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-rx-bus-width = <4>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ bootparam@0 { ++ reg = <0x00000000 0x040000>; ++ read-only; ++ }; ++ cr7@00040000 { ++ reg = <0x00040000 0x080000>; ++ read-only; ++ }; ++ cert_header_sa3@000C0000 { ++ reg = <0x000C0000 0x080000>; ++ read-only; ++ }; ++ bl2@00140000 { ++ reg = <0x00140000 0x040000>; ++ read-only; ++ }; ++ cert_header_sa6@00180000 { ++ reg = <0x00180000 0x040000>; ++ read-only; ++ }; ++ bl31@001C0000 { ++ reg = <0x001C0000 0x460000>; ++ read-only; ++ }; ++ uboot@00640000 { ++ reg = <0x00640000 0x0C0000>; ++ read-only; ++ }; ++ uboot-env@00700000 { ++ reg = <0x00700000 0x040000>; ++ read-only; ++ }; ++ dtb@00740000 { ++ reg = <0x00740000 0x080000>; ++ }; ++ kernel@007C0000 { ++ reg = <0x007C0000 0x1400000>; ++ }; ++ user@01BC0000 { ++ reg = <0x01BC0000 0x2440000>; ++ }; ++ }; ++ }; + }; + + &scif0 { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch new file mode 100644 index 0000000..07a55d5 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch @@ -0,0 +1,172 @@ +From 22f31ca58e24e9c8b92361e0d1f57a59d1fdbf64 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Fri, 23 Mar 2018 16:32:07 +0300 +Subject: [PATCH] r8a7797-v3msk,r8a7797-v3mzf: dts: Add spi flash s25fs512 + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts | 68 +++++++++++++++++++++++++++ + arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts | 68 +++++++++++++++++++++++++++ + 2 files changed, 136 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts b/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts +index 33c6c0d..604475d 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts ++++ b/arch/arm64/boot/dts/renesas/r8a7797-v3msk.dts +@@ -213,6 +213,74 @@ + function = "mmc"; + power-source = <3300>; + }; ++ ++ qspi0_pins: qspi0 { ++ groups = "qspi0_ctrl", "qspi0_data4"; ++ function = "qspi0"; ++ }; ++ ++ qspi1_pins: qspi1 { ++ groups = "qspi1_ctrl", "qspi1_data4"; ++ function = "qspi1"; ++ }; ++}; ++ ++&qspi0 { ++ pinctrl-0 = <&qspi0_pins &qspi1_pins>; ++ pinctrl-names = "default"; ++ ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "spansion,s25fs512s", "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-rx-bus-width = <4>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ bootparam@0 { ++ reg = <0x00000000 0x040000>; ++ read-only; ++ }; ++ cr7@00040000 { ++ reg = <0x00040000 0x080000>; ++ read-only; ++ }; ++ cert_header_sa3@000C0000 { ++ reg = <0x000C0000 0x080000>; ++ read-only; ++ }; ++ bl2@00140000 { ++ reg = <0x00140000 0x040000>; ++ read-only; ++ }; ++ cert_header_sa6@00180000 { ++ reg = <0x00180000 0x040000>; ++ read-only; ++ }; ++ bl31@001C0000 { ++ reg = <0x001C0000 0x460000>; ++ read-only; ++ }; ++ uboot@00640000 { ++ reg = <0x00640000 0x100000>; ++ read-only; ++ }; ++ dtb@00740000 { ++ reg = <0x00740000 0x080000>; ++ }; ++ kernel@007C0000 { ++ reg = <0x007C0000 0x1400000>; ++ }; ++ user@01BC0000 { ++ reg = <0x01BC0000 0x2440000>; ++ }; ++ }; ++ }; + }; + + &scif0 { +diff --git a/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts b/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts +index 71d7bad..4a64881 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts ++++ b/arch/arm64/boot/dts/renesas/r8a7797-v3mzf.dts +@@ -401,6 +401,74 @@ + function = "mmc"; + power-source = <3300>; + }; ++ ++ qspi0_pins: qspi0 { ++ groups = "qspi0_ctrl", "qspi0_data4"; ++ function = "qspi0"; ++ }; ++ ++ qspi1_pins: qspi1 { ++ groups = "qspi1_ctrl", "qspi1_data4"; ++ function = "qspi1"; ++ }; ++}; ++ ++&qspi0 { ++ pinctrl-0 = <&qspi0_pins &qspi1_pins>; ++ pinctrl-names = "default"; ++ ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "spansion,s25fs512s", "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-rx-bus-width = <4>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ bootparam@0 { ++ reg = <0x00000000 0x040000>; ++ read-only; ++ }; ++ cr7@00040000 { ++ reg = <0x00040000 0x080000>; ++ read-only; ++ }; ++ cert_header_sa3@000C0000 { ++ reg = <0x000C0000 0x080000>; ++ read-only; ++ }; ++ bl2@00140000 { ++ reg = <0x00140000 0x040000>; ++ read-only; ++ }; ++ cert_header_sa6@00180000 { ++ reg = <0x00180000 0x040000>; ++ read-only; ++ }; ++ bl31@001C0000 { ++ reg = <0x001C0000 0x460000>; ++ read-only; ++ }; ++ uboot@00640000 { ++ reg = <0x00640000 0x100000>; ++ read-only; ++ }; ++ dtb@00740000 { ++ reg = <0x00740000 0x080000>; ++ }; ++ kernel@007C0000 { ++ reg = <0x007C0000 0x1400000>; ++ }; ++ user@01BC0000 { ++ reg = <0x01BC0000 0x2440000>; ++ }; ++ }; ++ }; + }; + + &scif0 { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch new file mode 100644 index 0000000..4e39ed6 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0123-V3HSK-dts-Add-qspi-node.patch @@ -0,0 +1,101 @@ +From 2e5e9bce98281417fec62cb8af23c086c3d5b01a Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 5 Apr 2018 17:23:14 +0300 +Subject: [PATCH] V3HSK: dts: Add qspi node + +Signed-off-by: Dmitry Shifrin +--- + arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts | 73 +++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts b/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts +index bf8abe6..d863a95 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts ++++ b/arch/arm64/boot/dts/renesas/r8a7798-v3hsk.dts +@@ -225,6 +225,17 @@ + groups = "tpu_to0"; + function = "tpu"; + }; ++ ++ ++ qspi0_pins: qspi0 { ++ groups = "qspi0_ctrl", "qspi0_data4"; ++ function = "qspi0"; ++ }; ++ ++ qspi1_pins: qspi1 { ++ groups = "qspi1_ctrl", "qspi1_data4"; ++ function = "qspi1"; ++ }; + }; + + &scif0 { +@@ -356,3 +367,65 @@ + max-speed = <1000>; + }; + }; ++ ++&qspi0 { ++ pinctrl-0 = <&qspi0_pins &qspi1_pins>; ++ pinctrl-names = "default"; ++ ++ status = "okay"; ++ ++ flash@0 { ++ compatible = "spansion,s25fs512s", "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <50000000>; ++ spi-rx-bus-width = <4>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ bootparam@0 { ++ reg = <0x00000000 0x040000>; ++ read-only; ++ }; ++ cr7@00040000 { ++ reg = <0x00040000 0x080000>; ++ read-only; ++ }; ++ cert_header_sa3@000C0000 { ++ reg = <0x000C0000 0x080000>; ++ read-only; ++ }; ++ bl2@00140000 { ++ reg = <0x00140000 0x040000>; ++ read-only; ++ }; ++ cert_header_sa6@00180000 { ++ reg = <0x00180000 0x040000>; ++ read-only; ++ }; ++ bl31@001C0000 { ++ reg = <0x001C0000 0x460000>; ++ read-only; ++ }; ++ uboot@00640000 { ++ reg = <0x00640000 0x0C0000>; ++ read-only; ++ }; ++ uboot-env@00700000 { ++ reg = <0x00700000 0x040000>; ++ read-only; ++ }; ++ dtb@00740000 { ++ reg = <0x00740000 0x080000>; ++ }; ++ kernel@007C0000 { ++ reg = <0x007C0000 0x1400000>; ++ }; ++ user@01BC0000 { ++ reg = <0x01BC0000 0x2440000>; ++ }; ++ }; ++ }; ++}; +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch new file mode 100644 index 0000000..f347d47 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0124-RPC-Hyperflash-Add-devicetree-support.patch @@ -0,0 +1,219 @@ +From 466608ee2217ce02df6df977a35c4afa6ee1e350 Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Wed, 4 Apr 2018 12:29:47 +0300 +Subject: [PATCH 1/2] RPC: Hyperflash: Add devicetree support + +Signed-off-by: Dmitry Shifrin +--- + drivers/mtd/rpc_hyperflash.c | 142 +++++++++++++++++++++++-------------------- + 1 file changed, 77 insertions(+), 65 deletions(-) + +diff --git a/drivers/mtd/rpc_hyperflash.c b/drivers/mtd/rpc_hyperflash.c +index cf4d56e..c15e520 100644 +--- a/drivers/mtd/rpc_hyperflash.c ++++ b/drivers/mtd/rpc_hyperflash.c +@@ -15,8 +15,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + + /* RPC */ + #define RPC_BASE 0xEE200000 +@@ -156,10 +158,9 @@ + + struct rpc_info { + struct rw_semaphore lock; ++ struct clk *clk; + void __iomem *rpc_base; + void __iomem *flash_base; +- struct resource *rpc_res; +- struct resource *flash_res; + u32 flash_id; + struct mtd_info mtd; + }; +@@ -243,8 +244,6 @@ enum rpc_hf_size { + RPC_HF_SIZE_64BIT = RPC_SMENR_SPIDE(0xF), + }; + +-struct rpc_info *rpc_info; +- + static void rpc_hf_mode_man(struct rpc_info *info) + { + rpc_wait_tend(info); +@@ -861,8 +860,8 @@ static int rpc_hf_init_mtd(struct rpc_info *info) + rpc_hf_read_reg(info, 0x27 << 1, data, RPC_HF_SIZE_16BIT); + size = 1 << data[0]; + +- if (size > resource_size(info->flash_res)) +- size = resource_size(info->flash_res); ++ if (size > RPC_FLASH_SIZE) ++ size = RPC_FLASH_SIZE; + + if (size & (RPC_HF_ERASE_SIZE - 1)) { + retval = -EINVAL; +@@ -891,86 +890,99 @@ static int rpc_hf_init_mtd(struct rpc_info *info) + return retval; + } + +-static int rpc_flash_init(void) ++static int rpc_flash_probe(struct platform_device *pdev) + { +- struct rpc_info *info; ++ struct rpc_info *rpc; + struct resource *res; +- void __iomem *base; +- int retval = -ENODEV; +- +- if (!of_machine_is_compatible("renesas,r8a7795")) +- return -ENODEV; ++ int ret; + +- info = kzalloc(sizeof(*info), GFP_KERNEL); +- if (!info) ++ rpc = devm_kzalloc(&pdev->dev, sizeof(*rpc), GFP_KERNEL); ++ if (!rpc) + return -ENOMEM; + +- res = request_mem_region(RPC_BASE, RPC_SIZE, "RPC"); +- if (!res) +- goto out_info; ++ /* ...get memory */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ rpc->rpc_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(rpc->rpc_base)) { ++ dev_err(&pdev->dev, "cannot get resources\n"); ++ ret = PTR_ERR(rpc->rpc_base); ++ goto error; ++ } + +- info->rpc_res = res; +- base = ioremap(res->start, resource_size(res)); +- if (!base) +- goto out_rpc_res; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ rpc->flash_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(rpc->flash_base)) { ++ dev_err(&pdev->dev, "cannot get resources\n"); ++ ret = PTR_ERR(rpc->flash_base); ++ goto error; ++ } + +- info->rpc_base = base; +- res = request_mem_region(RPC_FLASH_BASE, RPC_FLASH_SIZE, "RPC-ext"); +- if (!res) +- goto out_rpc_base; ++ /* ...get clk */ ++ rpc->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rpc->clk)) { ++ dev_err(&pdev->dev, "cannot get clock\n"); ++ ret = PTR_ERR(rpc->clk); ++ goto error; ++ } + +- info->flash_res = res; +- base = ioremap(res->start, resource_size(res)); +- if (!base) +- goto out_flash_res; ++ /* ... enable clk */ ++ ret = clk_prepare_enable(rpc->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot prepare clock\n"); ++ goto error; ++ } ++ ++ platform_set_drvdata(pdev, rpc); + +- info->flash_base = base; +- retval = rpc_hf_init_mtd(info); +- if (retval) +- goto out_flash_base; ++ mtd_set_of_node(&rpc->mtd, pdev->dev.of_node); ++ ret = rpc_hf_init_mtd(rpc); ++ if (ret) { ++ dev_err(&pdev->dev, "mtd device register error.\n"); ++ goto error_clk_disable; ++ } + +- pr_info("HyperFlash Id: %x\n", info->flash_id); ++ dev_info(&pdev->dev, "HyperFlash Id: %x\n", rpc->flash_id); + +- rpc_info = info; + return 0; + +-out_flash_base: +- iounmap(info->flash_base); +-out_flash_res: +- release_mem_region(info->flash_res->start, +- resource_size(info->flash_res)); +-out_rpc_base: +- iounmap(info->rpc_base); +-out_rpc_res: +- release_mem_region(info->rpc_res->start, +- resource_size(info->rpc_res)); +-out_info: +- kfree(info); +- return retval; ++ ++error_clk_disable: ++ clk_disable_unprepare(rpc->clk); ++error: ++ return ret; + } + +-static void rpc_flash_exit(void) ++static int rpc_flash_exit(struct platform_device *pdev) + { +- struct rpc_info *info = rpc_info; ++ struct rpc_info *rpc = platform_get_drvdata(pdev); ++ ++ /* HW shutdown */ ++ clk_disable_unprepare(rpc->clk); ++ mtd_device_unregister(&rpc->mtd); ++ return 0; ++} + +- if (!info) +- return; + +- rpc_info = NULL; ++static const struct of_device_id rpc_flash_of_match[] = { ++ { .compatible = "renesas,rpc-hyperflash-r8a7798" }, ++ { .compatible = "renesas,rpc-hyperflash-r8a7797" }, ++ { }, ++}; + +- mtd_device_unregister(&info->mtd); ++MODULE_DEVICE_TABLE(of, rpc_flash_of_match); + +- iounmap(info->flash_base); +- release_mem_region(info->flash_res->start, +- resource_size(info->flash_res)); +- iounmap(info->rpc_base); +- release_mem_region(info->rpc_res->start, +- resource_size(info->rpc_res)); +- kfree(info); +-} ++/* platform driver interface */ ++static struct platform_driver rpc_flash_platform_driver = { ++ .probe = rpc_flash_probe, ++ .remove = rpc_flash_exit, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "rpc", ++ .of_match_table = of_match_ptr(rpc_flash_of_match), ++ }, ++}; + +-module_init(rpc_flash_init); +-module_exit(rpc_flash_exit); ++module_platform_driver(rpc_flash_platform_driver); + + MODULE_LICENSE("GPL v2"); + MODULE_DESCRIPTION("Renesas RPC HyperFlash MTD driver"); +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch new file mode 100644 index 0000000..08bee0d --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch @@ -0,0 +1,70 @@ +From 4b5dc172d87cc8291ddb7377d3252b6777cf8d4b Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Wed, 4 Apr 2018 12:59:59 +0300 +Subject: [PATCH 2/2] r8a7797: pinctrl: Add pin function for hyperflash + +Signed-off-by: Dmitry Shifrin +--- + drivers/pinctrl/sh-pfc/pfc-r8a7797.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c +index 4f451c4..d989acd 100644 +--- a/drivers/pinctrl/sh-pfc/pfc-r8a7797.c ++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7797.c +@@ -1196,6 +1196,25 @@ static const unsigned int qspi1_data4_mux[] = { + QSPI1_IO2_MARK, QSPI1_IO3_MARK + }; + ++static const unsigned int rpc_hyperflash_pins[] = { ++ RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 12), ++ RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 7), ++ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 9), ++ RCAR_GP_PIN(5, 10), RCAR_GP_PIN(5, 6), ++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 4), ++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 2), ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0) ++}; ++static const unsigned int rpc_hyperflash_mux[] = { ++ RPC_INT_N_MARK, RPC_RESET_N_MARK, ++ QSPI1_SSL_MARK, QSPI1_IO3_MARK, ++ QSPI1_IO2_MARK, QSPI1_MISO_IO1_MARK, ++ QSPI1_MOSI_IO0_MARK, QSPI1_SPCLK_MARK, ++ QSPI0_SSL_MARK, QSPI0_IO3_MARK, ++ QSPI0_IO2_MARK, QSPI0_MISO_IO1_MARK, ++ QSPI0_MOSI_IO0_MARK, QSPI0_SPCLK_MARK ++}; ++ + /* - I2C -------------------------------------------------------------------- */ + static const unsigned int i2c0_pins[] = { + /* SDA0, SCL0 */ +@@ -2030,6 +2049,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(qspi1_ctrl), + SH_PFC_PIN_GROUP(qspi1_data2), + SH_PFC_PIN_GROUP(qspi1_data4), ++ SH_PFC_PIN_GROUP(rpc_hyperflash), + }; + + static const char * const avb0_groups[] = { +@@ -2272,6 +2292,10 @@ static const char * const qspi1_groups[] = { + "qspi1_data4", + }; + ++static const char * const rpc_hyperflash_groups[] = { ++ "rpc_hyperflash", ++}; ++ + #define POCCTRL0 0x380 + #define POCCTRL1 0x384 + #define PIN2POCCTRL0_SHIFT(a) ({ \ +@@ -2317,6 +2341,7 @@ static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(vin1), + SH_PFC_FUNCTION(qspi0), + SH_PFC_FUNCTION(qspi1), ++ SH_PFC_FUNCTION(rpc_hyperflash), + }; + + static const struct pinmux_cfg_reg pinmux_config_regs[] = { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch new file mode 100644 index 0000000..0babaa4 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch @@ -0,0 +1,69 @@ +From 6633820967dcedde29b2e0507b7583c08901a12f Mon Sep 17 00:00:00 2001 +From: Dmitry Shifrin +Date: Thu, 5 Apr 2018 18:32:59 +0300 +Subject: [PATCH] r8a7798: pinctrl: Add pin function for hyperflash + +Signed-off-by: Dmitry Shifrin +--- + drivers/pinctrl/sh-pfc/pfc-r8a7798.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c +index f20afd1..f6f159e 100644 +--- a/drivers/pinctrl/sh-pfc/pfc-r8a7798.c ++++ b/drivers/pinctrl/sh-pfc/pfc-r8a7798.c +@@ -1525,6 +1525,24 @@ static const unsigned int qspi1_data4_mux[] = { + QSPI1_IO2_MARK, QSPI1_IO3_MARK + }; + ++static const unsigned int rpc_hyperflash_pins[] = { ++ RCAR_GP_PIN(5, 14), RCAR_GP_PIN(5, 12), ++ RCAR_GP_PIN(5, 11), RCAR_GP_PIN(5, 7), ++ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 9), ++ RCAR_GP_PIN(5, 10), RCAR_GP_PIN(5, 6), ++ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 4), ++ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 2), ++ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 0) ++}; ++static const unsigned int rpc_hyperflash_mux[] = { ++ RPC_INT_N_MARK, RPC_RESET_N_MARK, ++ QSPI1_SSL_MARK, QSPI1_IO3_MARK, ++ QSPI1_IO2_MARK, QSPI1_MISO_IO1_MARK, ++ QSPI1_MOSI_IO0_MARK, QSPI1_SPCLK_MARK, ++ QSPI0_SSL_MARK, QSPI0_IO3_MARK, ++ QSPI0_IO2_MARK, QSPI0_MISO_IO1_MARK, ++ QSPI0_MOSI_IO0_MARK, QSPI0_SPCLK_MARK ++}; + + /* - I2C -------------------------------------------------------------------- */ + static const unsigned int i2c0_pins[] = { +@@ -2479,6 +2497,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { + SH_PFC_PIN_GROUP(qspi1_ctrl), + SH_PFC_PIN_GROUP(qspi1_data2), + SH_PFC_PIN_GROUP(qspi1_data4), ++ SH_PFC_PIN_GROUP(rpc_hyperflash), + }; + + static const char * const avb_groups[] = { +@@ -2755,6 +2774,10 @@ static const char * const qspi1_groups[] = { + "qspi1_data4", + }; + ++static const char * const rpc_hyperflash_groups[] = { ++ "rpc_hyperflash", ++}; ++ + static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(avb), + SH_PFC_FUNCTION(gether), +@@ -2793,6 +2816,7 @@ static const struct sh_pfc_function pinmux_functions[] = { + SH_PFC_FUNCTION(vin1), + SH_PFC_FUNCTION(qspi0), + SH_PFC_FUNCTION(qspi1), ++ SH_PFC_FUNCTION(rpc_hyperflash), + }; + + static const struct pinmux_cfg_reg pinmux_config_regs[] = { +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg new file mode 100644 index 0000000..9a1c695 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/qspi.cfg @@ -0,0 +1,3 @@ +CONFIG_SPI_RENESAS_RPC=y +CONFIG_MTD_BLOCK=y +CONFIG_JFFS2_FS=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend index f5a1445..6beaec5 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend @@ -9,6 +9,7 @@ COMPATIBLE_MACHINE_v3hsk = "v3hsk" SRC_URI_append = " \ ${@bb.utils.contains('MACHINE_FEATURES', 'h3ulcb-had', ' file://hyperflash.cfg', '', d)} \ ${@base_conditional("SDHI_SEQ", "1", " file://sdhi_seq.cfg", "", d)} \ + file://qspi.cfg \ file://0001-spi-sh-msiof-fixes.patch \ file://0002-spi-spidev-add-spi-gpio-into-spidev.patch \ file://0003-spi-spi-gpio-fix-CPOL-mode.patch \ @@ -87,6 +88,23 @@ SRC_URI_append = " \ file://0107-V3H-device-tree-Add-VIP-devices-IRQs.patch \ file://0108-can-mcp251x-add-reset-gpio-support.patch \ file://0109-ASoC-R-Car-fix-incorrect-behavior-with-PulseAudio.patch \ + file://0110-Renesas-clk-Add-RPC-clock-source.patch \ + file://0111-Renesas-r8a7798-Add-RPC-clock.patch \ + file://0112-Renesas-r8a7798-pinctrl-Add-RPC-pin-control.patch \ + file://0113-Renesas-RPC-Add-RPC-driver.patch \ + file://0114-R8A7798-dtsi-Add-RPC-node-to-dtsi.patch \ + file://0115-R8A7798-condor-dts-Add-qspi-flash.patch \ + file://0116-spi-nor-Add-s25fs512-flash-support.patch \ + file://0117-spi-nor-Add-flash-array-support.patch \ + file://0118-r8a7797-clk-Add-rpc-clock.patch \ + file://0119-r8a7797-pinctrl-Add-qspi-pins.patch \ + file://0120-r8a7797-dtsi-Add-rpc-node.patch \ + file://0121-r8a7797-eagle-dts-Add-spi-flash-node.patch \ + file://0122-r8a7797-v3msk-r8a7797-v3mzf-dts-Add-spi-flash-s25fs5.patch \ + file://0123-V3HSK-dts-Add-qspi-node.patch \ + file://0124-RPC-Hyperflash-Add-devicetree-support.patch \ + file://0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch \ + file://0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch \ " SRC_URI_append_h3ulcb = " file://ulcb.cfg" -- cgit 1.2.3-korg From 10aa7f1f74b054c2575e7584a02b3738a24d0c7a Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Wed, 11 Apr 2018 17:06:13 +0300 Subject: Add IMR UIO support. V4L2 RCAR IMR driver is now a module. V4L2 driver is loaded by default. --- .../0127-IMR-UIO-Driver-initial-version.patch | 875 +++++++++++++++++++++ ...8-rcar_imr-v4l2-driver-Fix-module-support.patch | 321 ++++++++ .../recipes-kernel/linux/linux-renesas/condor.cfg | 1 - .../recipes-kernel/linux/linux-renesas/eagle.cfg | 1 - .../recipes-kernel/linux/linux-renesas/imr.cfg | 3 + .../linux/linux-renesas/salvator-x.cfg | 1 - .../recipes-kernel/linux/linux-renesas/ulcb.cfg | 1 - .../recipes-kernel/linux/linux-renesas/v3hsk.cfg | 1 - .../recipes-kernel/linux/linux-renesas/v3msk.cfg | 1 - .../recipes-kernel/linux/linux-renesas/v3mzf.cfg | 2 - .../linux/linux-renesas_4.9.bbappend | 9 + 11 files changed, 1208 insertions(+), 8 deletions(-) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0127-IMR-UIO-Driver-initial-version.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0128-rcar_imr-v4l2-driver-Fix-module-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/imr.cfg diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0127-IMR-UIO-Driver-initial-version.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0127-IMR-UIO-Driver-initial-version.patch new file mode 100644 index 0000000..74d6c07 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0127-IMR-UIO-Driver-initial-version.patch @@ -0,0 +1,875 @@ +From 4a0e94fec7f69a717fc084fa168c2120998f076c Mon Sep 17 00:00:00 2001 +From: Toshiya Tamaki +Date: Thu, 1 Jun 2017 15:43:21 +0900 +Subject: [PATCH 1/2] IMR UIO Driver initial version + +Signed-off-by: Toshiya Tamaki +--- + .../devicetree/bindings/imr/renesas,imr.txt | 55 +++ + arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi | 24 +- + arch/arm64/boot/dts/renesas/r8a7795.dtsi | 24 +- + arch/arm64/boot/dts/renesas/r8a7796.dtsi | 16 + + arch/arm64/boot/dts/renesas/r8a7797.dtsi | 24 +- + arch/arm64/boot/dts/renesas/r8a7798.dtsi | 36 +- + drivers/clk/renesas/r8a7796-cpg-mssr.c | 2 + + drivers/uio/Kconfig | 8 + + drivers/uio/Makefile | 1 + + drivers/uio/uio_imr.c | 495 +++++++++++++++++++++ + 10 files changed, 631 insertions(+), 54 deletions(-) + create mode 100644 Documentation/devicetree/bindings/imr/renesas,imr.txt + create mode 100644 drivers/uio/uio_imr.c + +diff --git a/Documentation/devicetree/bindings/imr/renesas,imr.txt b/Documentation/devicetree/bindings/imr/renesas,imr.txt +new file mode 100644 +index 0000000..50ce539 +--- /dev/null ++++ b/Documentation/devicetree/bindings/imr/renesas,imr.txt +@@ -0,0 +1,55 @@ ++* Renesas Electronics IMR ++ ++This file provides information on what the device node for the IMR ++interface contains. ++ ++Required properties: ++- compatible: "renesas,imr-r8a7795" if the device is a part of R8A7795 SoC. ++ "renesas,imr-r8a7796" if the device is a part of R8A7796 SoC. ++ "renesas,imr-r8a7797" if the device is a part of R8A7797 SoC. ++ ++ When compatible with the generic version, nodes must list the ++ SoC-specific version corresponding to the platform first ++ followed by the generic version. ++ ++- reg: offset and length of (1) the register block and (2) the stream buffer. ++- interrupts: A list of interrupt-specifiers, one for each entry in ++ interrupt-names. ++ If interrupt-names is not present, an interrupt specifier ++ for a single muxed interrupt. ++- clocks: clock phandle and specifier pair. ++- power-domains: must contain a reference to the PM domain. ++ ++Example: ++ ++ imr0{ ++ compatible = "renesas,imr-r8a7797"; ++ reg = <0 0xfe860000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 823>; ++ power-domains = <&sysc R8A7797_PD_A3VC>; ++ }; ++ ++ imr1{ ++ compatible = "renesas,imr-r8a7797"; ++ reg = <0 0xfe870000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 822>; ++ power-domains = <&sysc R8A7797_PD_A3VC>; ++ }; ++ ++ imr2{ ++ compatible = "renesas,imr-r8a7797"; ++ reg = <0 0xfe880000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 821>; ++ power-domains = <&sysc R8A7797_PD_A3VC>; ++ }; ++ ++ imr3{ ++ compatible = "renesas,imr-r8a7797"; ++ reg = <0 0xfe890000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 820>; ++ power-domains = <&sysc R8A7797_PD_A3VC>; ++ }; +diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +index 13516e9..745493c 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +@@ -2899,33 +2899,33 @@ + power-domains = <&sysc R8A7795_PD_A3IR>; + }; + +- imrlx4_ch0: imr-lx4@fe860000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe860000 0 0x2000>; ++ imrlx4_ch0: imr0@fe860000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch1: imr-lx4@fe870000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe870000 0 0x2000>; ++ imrlx4_ch1: imr1@fe870000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe870000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch2: imr-lx4@fe880000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe880000 0 0x2000>; ++ imrlx4_ch2: imr2@fe880000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe880000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch3: imr-lx4@fe890000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe890000 0 0x2000>; ++ imrlx4_ch3: imr3@fe890000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe890000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7795_PD_A3VC>; +diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi +index 565beb9..96e182a 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi +@@ -2895,33 +2895,33 @@ + power-domains = <&sysc R8A7795_PD_A3IR>; + }; + +- imrlx4_ch0: imr-lx4@fe860000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe860000 0 0x2000>; ++ imrlx4_ch0: imr0@fe860000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch1: imr-lx4@fe870000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe870000 0 0x2000>; ++ imrlx4_ch1: imr1@fe870000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe870000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch2: imr-lx4@fe880000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe880000 0 0x2000>; ++ imrlx4_ch2: imr2@fe880000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe880000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7795_PD_A3VC>; + }; + +- imrlx4_ch3: imr-lx4@fe890000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe890000 0 0x2000>; ++ imrlx4_ch3: imr3@fe890000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; ++ reg = <0 0xfe890000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7795_PD_A3VC>; +diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi +index bf37b8a..b747f0c 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi +@@ -1174,6 +1174,22 @@ + status = "disabled"; + }; + ++ imrlx4_ch0: imr0@fe860000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7796"; ++ reg = <0 0xfe860000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 823>; ++ power-domains = <&sysc R8A7796_PD_A3VC>; ++ }; ++ ++ imrlx4_ch1: imr1@fe870000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7796"; ++ reg = <0 0xfe870000 0 0x10000>; ++ interrupts = ; ++ clocks = <&cpg CPG_MOD 822>; ++ power-domains = <&sysc R8A7796_PD_A3VC>; ++ }; ++ + can0: can@e6c30000 { + compatible = "renesas,can-r8a7796", + "renesas,rcar-gen3-can"; +diff --git a/arch/arm64/boot/dts/renesas/r8a7797.dtsi b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +index 2beca53..c878467 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +@@ -1239,33 +1239,33 @@ + power-domains = <&sysc R8A7797_PD_A3IR>; + }; + +- imrlx4_ch0: imr-lx4@fe860000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe860000 0 0x2000>; ++ imrlx4_ch0: imr0@fe860000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7797"; ++ reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; + }; + +- imrlx4_ch1: imr-lx4@fe870000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe870000 0 0x2000>; ++ imrlx4_ch1: imr1@fe870000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7797"; ++ reg = <0 0xfe870000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; + }; + +- imrlx4_ch2: imr-lx4@fe880000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe880000 0 0x2000>; ++ imrlx4_ch2: imr2@fe880000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7797"; ++ reg = <0 0xfe880000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; + }; + +- imrlx4_ch3: imr-lx4@fe890000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe890000 0 0x2000>; ++ imrlx4_ch3: imr3@fe890000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7797"; ++ reg = <0 0xfe890000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; +diff --git a/arch/arm64/boot/dts/renesas/r8a7798.dtsi b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +index 48ce2af..7bfd0483 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7798.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +@@ -1588,50 +1588,50 @@ + power-domains = <&sysc R8A7798_PD_A3IR>; + }; + +- imrlx4_ch0: imr-lx4@fe860000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe860000 0 0x2000>; ++ imrlx4_ch0: imr0@fe860000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; + }; + +- imrlx4_ch1: imr-lx4@fe870000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe870000 0 0x2000>; ++ imrlx4_ch1: imr1@fe870000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe870000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; + }; + +- imrlx4_ch2: imr-lx4@fe880000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe880000 0 0x2000>; ++ imrlx4_ch2: imr2@fe880000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe880000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; + }; + +- imrlx4_ch3: imr-lx4@fe890000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe890000 0 0x2000>; ++ imrlx4_ch3: imr3@fe890000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe890000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; + }; + +- imrlx4_ch4: imr-lx4@fe8a0000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe8a0000 0 0x2000>; ++ imrlx4_ch4: imr4@fe8a0000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe8a0000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 707>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; + rse; + }; + +- imrlx4_ch5: imr-lx4@fe8b0000 { +- compatible = "renesas,imr-lx4"; +- reg = <0 0xfe8b0000 0 0x2000>; ++ imrlx4_ch5: imr5@fe8b0000 { ++ compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; ++ reg = <0 0xfe8b0000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 706>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; +diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c +index e886d8a..e2ca77c 100644 +--- a/drivers/clk/renesas/r8a7796-cpg-mssr.c ++++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c +@@ -204,6 +204,8 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { + DEF_MOD("vin1", 810, R8A7796_CLK_S0D2), + DEF_MOD("vin0", 811, R8A7796_CLK_S0D2), + DEF_MOD("etheravb", 812, R8A7796_CLK_S0D6), ++ DEF_MOD("imr1", 822, R8A7796_CLK_S2D1), ++ DEF_MOD("imr0", 823, R8A7796_CLK_S2D1), + DEF_MOD("imp", 824, R8A7796_CLK_S1D1), + DEF_MOD("gpio7", 905, R8A7796_CLK_S3D4), + DEF_MOD("gpio6", 906, R8A7796_CLK_S3D4), +diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig +index 52c98ce..09d91ac 100644 +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -155,4 +155,12 @@ config UIO_MF624 + + If you compile this as a module, it will be called uio_mf624. + ++config UIO_IMR ++ tristate "Renesas IMR support" ++ ++ help ++ Renesas IMR device driver. ++ This driver supports the following SoCs: ++ - R8A7795, R8A7796, R8A7797. ++ + endif +diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile +index 8560dad..3d29324 100644 +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_UIO_NETX) += uio_netx.o + obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o + obj-$(CONFIG_UIO_MF624) += uio_mf624.o + obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o ++obj-$(CONFIG_UIO_IMR) += uio_imr.o +diff --git a/drivers/uio/uio_imr.c b/drivers/uio/uio_imr.c +new file mode 100644 +index 0000000..a64c65e +--- /dev/null ++++ b/drivers/uio/uio_imr.c +@@ -0,0 +1,495 @@ ++/*************************************************************************/ /* ++ IMR ++ ++ Copyright (C) 2015-2017 Renesas Electronics Corporation ++ ++ License Dual MIT/GPLv2 ++ ++ The contents of this file are subject to the MIT license as set out below. ++ ++ 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, sublicense, 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 shall be included in ++ all copies or substantial portions of the Software. ++ ++ Alternatively, the contents of this file may be used under the terms of ++ the GNU General Public License Version 2 ("GPL") in which case the provisions ++ of GPL are applicable instead of those above. ++ ++ If you wish to allow use of your version of this file only under the terms of ++ GPL, and not to allow others to use your version of this file under the terms ++ of the MIT license, indicate your decision by deleting the provisions above ++ and replace them with the notice and other provisions required by GPL as set ++ out in the file called "GPL-COPYING" included in this distribution. If you do ++ not delete the provisions above, a recipient may use your version of this file ++ under the terms of either the MIT license or GPL. ++ ++ This License is also included in this distribution in the file called ++ "MIT-COPYING". ++ ++ EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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 NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR ++ COPYRIGHT HOLDERS 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. ++ ++ ++ GPLv2: ++ If you wish to use this file under the terms of GPL, following terms are ++ effective. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2 of the License. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++*/ /*************************************************************************/ ++ ++/* PRQA S 292,2212,2214 EOF */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* IMR uio driver name */ ++#define DRIVER_NAME "uio_imr" ++ ++/* IMR register definition */ ++#define IMR_REG_IMR_ADDRESS (0x018U) ++#define IMR_REG_IMR_BIT_BASE (0x3U << 3) ++#define IMR_REG_IMR_BIT_INT (0x1U << 2) ++#define IMR_REG_IMR_BIT_IER (0x1U << 1) ++#define IMR_REG_IMR_BIT_TRA (0x1U << 0) ++ ++/** ++ * struct uio_platdata - the uio platform data structure ++ * @uioinfo: UIO device capabilities ++ * @lock lock flag for irq. ++ * @flags: flags for request_irq. ++ * @pdev: IMR platform device data. ++ * @base_reg: IMR base register address. ++ * @clk: clock data. ++ * ++ * the uio platform data structure. ++ */ ++struct uio_platdata { ++ struct uio_info *uioinfo; ++ spinlock_t lock; ++ unsigned long flags; ++ struct platform_device *pdev; ++ void __iomem *base_reg; ++ struct clk *clock; ++}; ++ ++static void write_register(struct uio_platdata *priv, ++ u32 reg_offs, u32 data); ++static int uio_imr_open(struct uio_info *info, ++ __attribute__((unused)) struct inode *inode); ++static int uio_imr_release(struct uio_info *info, ++ __attribute__((unused)) struct inode *inode); ++static irqreturn_t uio_imr_handler(__attribute__((unused)) int irq, ++ struct uio_info *dev_info); ++static int uio_imr_irqcontrol(struct uio_info *info, s32 irq_on); ++static int uio_imr_probe(struct platform_device *pdev); ++static int uio_imr_remove(struct platform_device *pdev); ++static int uio_runtime_imr_nop(__attribute__((unused)) struct device *dev); ++ ++/** ++ * write_register() - register setting ++ * @priv: uio platform data. ++ * @reg_offs: register offset. ++ * @data: register value. ++ * ++ * register setting. ++ * ++ * ++ * Return: none ++ */ ++static void write_register(struct uio_platdata *priv, ++ u32 reg_offs, u32 data) ++{ ++ iowrite32(data, (u8 *)priv->base_reg + reg_offs); /* PRQA S 488 */ ++} ++ ++/** ++ * uio_imr_open() - open imr module ++ * @info: UIO device capabilities. ++ * @inode: inode. ++ * ++ * Open imr module. ++ * ++ * ++ * Return: 0 normal end. ++ */ ++/* PRQA S 3206 2 */ ++static int uio_imr_open(struct uio_info *info, ++ __attribute__((unused)) struct inode *inode) ++{ ++ struct uio_platdata *pdata = info->priv; ++ /* PRQA S 3200 1 */ ++ pr_debug("uio_imr_open enter. name=%s\n", pdata->uioinfo->name); ++ ++ /* Wait until the Runtime PM code has woken up the device */ ++ (void)pm_runtime_get_sync(&pdata->pdev->dev); ++ ++ return 0; ++} ++ ++/** ++ * uio_imr_release() - close imr module ++ * @info: UIO device capabilities. ++ * @inode: inode. ++ * ++ * Close imr module. ++ * ++ * ++ * Return: 0 normal end. ++ */ ++/* PRQA S 3206 2 */ ++static int uio_imr_release(struct uio_info *info, ++ __attribute__((unused)) struct inode *inode) ++{ ++ struct uio_platdata *pdata = info->priv; ++ ++ pr_debug("uio_imr_release enter\n"); /* PRQA S 3200 */ ++ ++ /* Tell the Runtime PM code that the device has become idle */ ++ (void)pm_runtime_put_sync(&pdata->pdev->dev); ++ ++ return 0; ++} ++ ++/** ++ * uio_imr_handler() - IMR interrupt handler ++ * @irq: irq No. ++ * @dev_info: UIO device capabilities. ++ * ++ * IMR interrupt handler. ++ * ++ * ++ * Return: IRQ_HANDLED normal end. ++ */ ++/* PRQA S 3206 1*/ ++static irqreturn_t uio_imr_handler(__attribute__((unused))int irq, ++ struct uio_info *dev_info) ++{ ++ struct uio_platdata *pdata = dev_info->priv; ++ ++ ++ pr_debug("uio_imr_handler enter\n"); /* PRQA S 3200 */ ++ ++ /* Mask interrupt */ ++ write_register(pdata, IMR_REG_IMR_ADDRESS, ++ IMR_REG_IMR_BIT_BASE | (IMR_REG_IMR_BIT_INT | ++ IMR_REG_IMR_BIT_IER | IMR_REG_IMR_BIT_TRA)); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * uio_imr_irqcontrol() - IMR irq controller ++ * @info: UIO device capabilities. ++ * @irq_on: irq enable/disable. ++ * ++ * IMR irq controller. Enable and disable the interrupt. ++ * ++ * ++ * Return: 0 normal end. ++ */ ++static int uio_imr_irqcontrol(struct uio_info *info, s32 irq_on) ++{ ++ struct uio_platdata *pdata = info->priv; ++ u64 flag; ++ ++ pr_debug("uio_imr_irqcontrol enter\n"); /* PRQA S 3200 */ ++ ++ spin_lock_irqsave(&pdata->lock, flag); ++ if (irq_on != 0) { ++ if (test_and_clear_bit(0, &pdata->flags) != 0) ++ enable_irq((u32)info->irq); ++ } else { ++ if (test_and_set_bit(0, &pdata->flags) == 0) ++ disable_irq((u32)info->irq); ++ } ++ spin_unlock_irqrestore(&pdata->lock, flag); ++ ++ return 0; ++} ++ ++/* PRQA S 1053,1041,605 10 */ ++static const struct of_device_id rcar_imr_dt_ids[] = { ++ { .compatible = "renesas,imr-r8a7795", .data = 0 }, ++ { .compatible = "renesas,imr-r8a7796", .data = 0 }, ++ { .compatible = "renesas,imr-r8a7797", .data = 0 }, ++ { .compatible = "renesas,imr-r8a7798", .data = 0 }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rcar_imr_dt_ids); /* PRQA S 605 */ ++ ++/** ++ * uio_imr_probe() - Initialize IMR module. ++ * @pdev: platform device data. ++ * ++ * Initialize IMR module. ++ * ++ * ++ * Return: 0 normal end. ++ * -EINVAL parameter error. ++ * -ENOMEM memory error. ++ * -ENODEV system error. ++ */ ++static int uio_imr_probe(struct platform_device *pdev) ++{ ++ struct uio_info *uioinfo_data = NULL; ++ struct uio_platdata *pdata; ++ struct uio_mem *uiomem; ++ int ret = 0; ++ unsigned int i; ++ struct resource *rsc; ++ unsigned int irq_l; ++ unsigned long remap_size; ++ ++ if (pdev == NULL) { ++ pr_err("missing pdev\n"); ++ ret = -EINVAL; ++ } else { ++ if (pdev->dev.of_node == NULL) { ++ dev_err(&pdev->dev, "missing pdev->dev.of_node\n"); ++ ret = -EINVAL; ++ } ++ } ++ ++ if (ret == 0) { ++ pr_debug("uio_imr_probe enter name = %s\n", /* PRQA S 3200 */ ++ pdev->dev.of_node->name); ++ ++ uioinfo_data = devm_kzalloc(&pdev->dev, ++ sizeof(*uioinfo_data), ++ GFP_KERNEL); ++ if (uioinfo_data == NULL) ++ ret = -ENOMEM; ++ } ++ ++ if (ret == 0) { ++ uioinfo_data->name = pdev->dev.of_node->name; ++ uioinfo_data->version = "0.1"; ++ ++ /* get irq number */ ++ irq_l = irq_of_parse_and_map(pdev->dev.of_node, 0); ++ if ((int)irq_l == -ENXIO) ++ uioinfo_data->irq = platform_get_irq(pdev, 0); ++ else ++ uioinfo_data->irq = (int)irq_l; ++ ++ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); ++ if (pdata == NULL) ++ ret = -ENOMEM; ++ } ++ ++ if (ret == 0) { ++ pdata->uioinfo = uioinfo_data; ++ spin_lock_init(&pdata->lock); /* PRQA S 3200 */ ++ pdata->flags = 0; ++ pdata->pdev = pdev; ++ ++ uiomem = &uioinfo_data->mem[0]; ++ ++ for (i = 0; i < pdev->num_resources; ++i) { ++ /* PRQA S 491 1 */ ++ struct resource *r = &pdev->resource[i]; ++ ++ if (r->flags == IORESOURCE_IRQ) { ++ uioinfo_data->irq = (long)r->start; ++ } else if (r->flags != IORESOURCE_MEM) { ++ ; ++ } else { ++ if (uiomem >= ++ &uioinfo_data->mem[MAX_UIO_MAPS]) { ++ dev_warn(&pdev->dev, ++ "device has more than " ++ __stringify(MAX_UIO_MAPS) ++ " I/O memory resources.\n"); ++ break; ++ } ++ ++ uiomem->memtype = UIO_MEM_PHYS; ++ uiomem->addr = r->start; ++ uiomem->size = (r->end - r->start) + 1; ++ ++uiomem; /* PRQA S 489 */ ++ } ++ } ++ ++ while (uiomem < &uioinfo_data->mem[MAX_UIO_MAPS]) { ++ uiomem->size = 0; ++ ++uiomem; /* PRQA S 489 */ ++ } ++ ++ uioinfo_data->handler = &uio_imr_handler; ++ uioinfo_data->irqcontrol = &uio_imr_irqcontrol; ++ uioinfo_data->open = &uio_imr_open; ++ uioinfo_data->release = &uio_imr_release; ++ uioinfo_data->priv = pdata; ++ ++ pm_runtime_enable(&pdev->dev); ++ ++ ret = uio_register_device(&pdev->dev, pdata->uioinfo); ++ if (ret != 0) { ++ pm_runtime_disable(&pdev->dev); ++ dev_err(&pdev->dev, "could not register uio device\n"); ++ ret = -ENODEV; ++ } ++ } ++ ++ if (ret == 0) { ++ platform_set_drvdata(pdev, pdata); ++ pdata->clock = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(pdata->clock)) { ++ pm_runtime_disable(&pdev->dev); ++ dev_err(&pdev->dev, "could not get clock\n"); ++ ret = -ENODEV; ++ } else { ++ /* clock enable */ ++ (void)clk_prepare_enable(pdata->clock); ++ } ++ } ++ ++ if (ret == 0) { ++ rsc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (rsc == 0) { ++ pm_runtime_disable(&pdev->dev); ++ dev_err(&pdev->dev, "could not platform_get_resource\n"); ++ ret = -ENODEV; ++ } ++ } ++ ++ if (ret == 0) { ++ remap_size = (rsc->end - rsc->start) + 1; ++ if (!request_mem_region(rsc->start, ++ remap_size, ++ uioinfo_data->name)) { ++ dev_err(&pdev->dev, "could not request IO\n"); ++ pm_runtime_disable(&pdev->dev); ++ ret = -ENOMEM; ++ } ++ } ++ ++ if (ret == 0) { ++ /* IMR Register Adderss */ ++ pdata->base_reg = devm_ioremap_nocache(&pdev->dev, ++ rsc->start, ++ remap_size); ++ if (pdata->base_reg == NULL) { ++ release_mem_region(rsc->start, resource_size(rsc)); ++ dev_err(&pdev->dev, "could not remap IMR register\n"); ++ pm_runtime_disable(&pdev->dev); ++ ret = -ENOMEM; ++ } else { ++ /* PRQA S 3200 2 */ ++ pr_debug("IMR reg_base = %x size = %x\n", ++ (uint32_t)(rsc->start), (uint32_t)remap_size); ++ } ++ } ++ ++ return ret; ++} ++ ++/** ++ * uio_imr_remove() - release IMR module. ++ * @pdev: platform device data. ++ * ++ * release IMR module. ++ * ++ * ++ * Return: 0 normal end. ++ */ ++static int uio_imr_remove(struct platform_device *pdev) ++{ ++ struct resource *rsc; ++ struct uio_platdata *pdata = platform_get_drvdata(pdev); ++ ++ /* PRQA S 3200 1 */ ++ pr_debug("uio_imr_remove enter name = %s\n", pdata->uioinfo->name); ++ ++ clk_disable_unprepare(pdata->clock); ++ ++ uio_unregister_device(pdata->uioinfo); ++ ++ pm_runtime_disable(&pdev->dev); ++ ++ irq_dispose_mapping((u32)pdata->uioinfo->irq); ++ ++ pdata->uioinfo->handler = NULL; ++ pdata->uioinfo->irqcontrol = NULL; ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (pdata->base_reg != NULL) ++ devm_iounmap(&pdev->dev, pdata->base_reg); ++ ++ rsc = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (rsc != 0) ++ release_mem_region(rsc->start, resource_size(rsc)); ++ ++ return 0; ++} ++ ++/** ++ * uio_runtime_imr_nop() - Runtime PM callback function. ++ * @dev: device data. ++ * ++ * Runtime PM callback function. ++ * ++ * ++ * Return: 0 normal end. ++ */ ++ /* PRQA S 3206 1 */ ++static int uio_runtime_imr_nop(__attribute__((unused)) struct device *dev) ++{ ++ pr_debug("uio_runtime_imr_nop enter\n"); /* PRQA S 3200 */ ++ return 0; ++} ++/* PRQA S 1053 4 */ ++static const struct dev_pm_ops uio_dev_pm_imr_ops = { ++ .runtime_suspend = &uio_runtime_imr_nop, ++ .runtime_resume = &uio_runtime_imr_nop, ++}; ++ ++static struct platform_driver uio_imr_platform_driver = { ++ .probe = &uio_imr_probe, ++ .remove = &uio_imr_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = &uio_dev_pm_imr_ops, ++ .of_match_table = of_match_ptr(rcar_imr_dt_ids), ++ }, ++}; ++ ++module_platform_driver(uio_imr_platform_driver); ++ ++ ++MODULE_AUTHOR("Renesas Electronics Corporation"); ++MODULE_DESCRIPTION("Userspace I/O driver for IMR"); ++MODULE_LICENSE("Dual MIT/GPL"); ++MODULE_ALIAS("platform:" DRIVER_NAME); +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0128-rcar_imr-v4l2-driver-Fix-module-support.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0128-rcar_imr-v4l2-driver-Fix-module-support.patch new file mode 100644 index 0000000..f4ee6e2 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0128-rcar_imr-v4l2-driver-Fix-module-support.patch @@ -0,0 +1,321 @@ +From 772299b20e32fc3ff86de4a83845e8246d97bcbb Mon Sep 17 00:00:00 2001 +From: Andrey Dolnikov +Date: Mon, 9 Apr 2018 12:50:23 +0300 +Subject: [PATCH 2/2] rcar_imr v4l2 driver: Fix module support. + +--- + arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi | 8 +++++++ + arch/arm64/boot/dts/renesas/r8a7795.dtsi | 8 +++++++ + arch/arm64/boot/dts/renesas/r8a7796.dtsi | 6 ++++++ + arch/arm64/boot/dts/renesas/r8a7797.dtsi | 8 +++++++ + arch/arm64/boot/dts/renesas/r8a7798.dtsi | 10 +++++++++ + drivers/media/platform/rcar_imr.c | 32 +++++++++++++++++++++++++--- + 6 files changed, 69 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +index 745493c..f2769c2 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +@@ -2899,12 +2899,17 @@ + power-domains = <&sysc R8A7795_PD_A3IR>; + }; + ++ imr_v4l2_alloc: imr_alloc { ++ dma-coherent; ++ }; ++ + imrlx4_ch0: imr0@fe860000 { + compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; + reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch1: imr1@fe870000 { +@@ -2913,6 +2918,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch2: imr2@fe880000 { +@@ -2921,6 +2927,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch3: imr3@fe890000 { +@@ -2929,6 +2936,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + }; + }; +diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi +index 96e182a..366ee5f 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi +@@ -2895,12 +2895,17 @@ + power-domains = <&sysc R8A7795_PD_A3IR>; + }; + ++ imr_v4l2_alloc: imr_alloc { ++ dma-coherent; ++ }; ++ + imrlx4_ch0: imr0@fe860000 { + compatible = "renesas,imr-lx4", "renesas,imr-r8a7795"; + reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch1: imr1@fe870000 { +@@ -2909,6 +2914,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch2: imr2@fe880000 { +@@ -2917,6 +2923,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch3: imr3@fe890000 { +@@ -2925,6 +2932,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7795_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + }; + }; +diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi +index b747f0c..442f027 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi +@@ -1174,12 +1174,17 @@ + status = "disabled"; + }; + ++ imr_v4l2_alloc: imr_alloc { ++ dma-coherent; ++ }; ++ + imrlx4_ch0: imr0@fe860000 { + compatible = "renesas,imr-lx4", "renesas,imr-r8a7796"; + reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7796_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch1: imr1@fe870000 { +@@ -1188,6 +1193,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7796_PD_A3VC>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + can0: can@e6c30000 { +diff --git a/arch/arm64/boot/dts/renesas/r8a7797.dtsi b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +index c878467..16e73b4 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7797.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7797.dtsi +@@ -1239,12 +1239,17 @@ + power-domains = <&sysc R8A7797_PD_A3IR>; + }; + ++ imr_v4l2_alloc: imr_alloc { ++ dma-coherent; ++ }; ++ + imrlx4_ch0: imr0@fe860000 { + compatible = "renesas,imr-lx4", "renesas,imr-r8a7797"; + reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch1: imr1@fe870000 { +@@ -1253,6 +1258,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch2: imr2@fe880000 { +@@ -1261,6 +1267,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch3: imr3@fe890000 { +@@ -1269,6 +1276,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + }; + }; +diff --git a/arch/arm64/boot/dts/renesas/r8a7798.dtsi b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +index 7bfd0483..0742ec0 100644 +--- a/arch/arm64/boot/dts/renesas/r8a7798.dtsi ++++ b/arch/arm64/boot/dts/renesas/r8a7798.dtsi +@@ -1588,12 +1588,17 @@ + power-domains = <&sysc R8A7798_PD_A3IR>; + }; + ++ imr_v4l2_alloc: imr_alloc { ++ dma-coherent; ++ }; ++ + imrlx4_ch0: imr0@fe860000 { + compatible = "renesas,imr-lx4", "renesas,imr-r8a7798"; + reg = <0 0xfe860000 0 0x10000>; + interrupts = ; + clocks = <&cpg CPG_MOD 823>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch1: imr1@fe870000 { +@@ -1602,6 +1607,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 822>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch2: imr2@fe880000 { +@@ -1610,6 +1616,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 821>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch3: imr3@fe890000 { +@@ -1618,6 +1625,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + }; + + imrlx4_ch4: imr4@fe8a0000 { +@@ -1626,6 +1634,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 707>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + rse; + }; + +@@ -1635,6 +1644,7 @@ + interrupts = ; + clocks = <&cpg CPG_MOD 706>; + power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ alloc-dev = <&imr_v4l2_alloc>; + rse; + }; + +diff --git a/drivers/media/platform/rcar_imr.c b/drivers/media/platform/rcar_imr.c +index 7b16765..3558211 100644 +--- a/drivers/media/platform/rcar_imr.c ++++ b/drivers/media/platform/rcar_imr.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1917,6 +1918,8 @@ static int imr_probe(struct platform_device *pdev) + struct resource *res; + struct device_node *np = pdev->dev.of_node; + int ret; ++ phandle *prop; ++ struct device_node *node; + + imr = devm_kzalloc(&pdev->dev, sizeof(*imr), GFP_KERNEL); + if (!imr) +@@ -1989,10 +1992,15 @@ static int imr_probe(struct platform_device *pdev) + ret = PTR_ERR(adev); + goto m2m_init_rollback; + } ++ + adev->dma_mask = &adev->coherent_dma_mask; + adev->coherent_dma_mask = DMA_BIT_MASK(32); +- arch_setup_dma_ops(adev, 0, DMA_BIT_MASK(32) + 1, NULL, true); + imr->alloc_dev = adev; ++ prop = of_get_property(np, "alloc-dev", NULL); ++ if (prop) { ++ node = of_find_node_by_phandle(be32_to_cpup(prop)); ++ of_dma_configure(adev, node); ++ } + + strlcpy(imr->video_dev.name, dev_name(&pdev->dev), sizeof(imr->video_dev.name)); + imr->video_dev.fops = &imr_fops; +@@ -2032,7 +2040,6 @@ static int imr_remove(struct platform_device *pdev) + + //pm_runtime_disable(imr->v4l2_dev.dev); + video_unregister_device(&imr->video_dev); +- //device_destroy(imr->alloc_dev, MKDEV(0, 0)); + v4l2_m2m_release(imr->m2m_dev); + v4l2_device_unregister(&imr->v4l2_dev); + +@@ -2100,7 +2107,26 @@ static struct platform_driver imr_platform_driver = { + }, + }; + +-module_platform_driver(imr_platform_driver); ++static int __init imr_module_init(void) ++{ ++ return platform_driver_register(&imr_platform_driver); ++} ++ ++static int imr_device_destroy(struct device *dev, void *data) ++{ ++ device_destroy(imr_alloc_class, dev->devt); ++ return 0; ++} ++ ++static void __exit imr_module_exit(void) ++{ ++ class_for_each_device(imr_alloc_class, NULL, NULL, imr_device_destroy); ++ class_destroy(imr_alloc_class); ++ platform_driver_unregister(&imr_platform_driver); ++} ++ ++module_init(imr_module_init); ++module_exit(imr_module_exit); + + MODULE_ALIAS("imr"); + MODULE_AUTHOR("Cogent Embedded Inc. "); +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/condor.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/condor.cfg index 7322e21..54ab6a7 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/condor.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/condor.cfg @@ -20,7 +20,6 @@ CONFIG_SOC_CAMERA_SCALE_CROP=y CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_SOC_CAMERA_MAX9286=y CONFIG_SOC_CAMERA_OV106XX=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_UINPUT=y CONFIG_TOUCHSCREEN_PROPERTIES=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/eagle.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/eagle.cfg index ca8cee8..1e84e31 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/eagle.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/eagle.cfg @@ -20,7 +20,6 @@ CONFIG_SOC_CAMERA_SCALE_CROP=y CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_SOC_CAMERA_MAX9286=y CONFIG_SOC_CAMERA_OV106XX=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_UINPUT=y CONFIG_TOUCHSCREEN_PROPERTIES=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/imr.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/imr.cfg new file mode 100644 index 0000000..cc7f69e --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/imr.cfg @@ -0,0 +1,3 @@ +CONFIG_VIDEO_RENESAS_IMR=m +CONFIG_UIO=y +CONFIG_UIO_IMR=m diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/salvator-x.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/salvator-x.cfg index 36c6103..4af8122 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/salvator-x.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/salvator-x.cfg @@ -24,7 +24,6 @@ CONFIG_USB_XHCI_PLATFORM=y CONFIG_USB_XHCI_RCAR=y CONFIG_MMC_SDHI_PRE_REQ=y CONFIG_MMC_SDHI_SEQ=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_PROPERTIES=y CONFIG_HID_MULTITOUCH=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/ulcb.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/ulcb.cfg index 5442f19..2714452 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/ulcb.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/ulcb.cfg @@ -37,7 +37,6 @@ CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_PLATFORM=y CONFIG_USB_XHCI_RCAR=y CONFIG_USB_ACM=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_VIRTIO_RCAR_PCIE=y CONFIG_BT=y CONFIG_BT_BNEP=m diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3hsk.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3hsk.cfg index 3713001..b842d14 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3hsk.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3hsk.cfg @@ -24,7 +24,6 @@ CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_SOC_CAMERA_MAX9286=y CONFIG_SOC_CAMERA_TI9X4=y CONFIG_SOC_CAMERA_OV106XX=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_UINPUT=y CONFIG_TOUCHSCREEN_PROPERTIES=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3msk.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3msk.cfg index a3c72d5..e349d80 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3msk.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3msk.cfg @@ -24,7 +24,6 @@ CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_SOC_CAMERA_MAX9286=y CONFIG_SOC_CAMERA_TI9X4=y CONFIG_SOC_CAMERA_OV106XX=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_UINPUT=y CONFIG_TOUCHSCREEN_PROPERTIES=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3mzf.cfg b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3mzf.cfg index cd41866..d1b8914 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3mzf.cfg +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/v3mzf.cfg @@ -17,9 +17,7 @@ CONFIG_SOC_CAMERA_SCALE_CROP=y CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_SOC_CAMERA_TI9X4=y CONFIG_SOC_CAMERA_OV106XX=y -CONFIG_VIDEO_RENESAS_IMR=y CONFIG_SERIAL_SH_SCI_DMA=y -CONFIG_UIO=y CONFIG_SPI_SLAVE=y CONFIG_SPI_SLAVE_TIME=y CONFIG_SPI_SLAVE_SYSTEM_CONTROL=y diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend index 6beaec5..0233afa 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend @@ -10,6 +10,7 @@ SRC_URI_append = " \ ${@bb.utils.contains('MACHINE_FEATURES', 'h3ulcb-had', ' file://hyperflash.cfg', '', d)} \ ${@base_conditional("SDHI_SEQ", "1", " file://sdhi_seq.cfg", "", d)} \ file://qspi.cfg \ + file://imr.cfg \ file://0001-spi-sh-msiof-fixes.patch \ file://0002-spi-spidev-add-spi-gpio-into-spidev.patch \ file://0003-spi-spi-gpio-fix-CPOL-mode.patch \ @@ -105,6 +106,8 @@ SRC_URI_append = " \ file://0124-RPC-Hyperflash-Add-devicetree-support.patch \ file://0125-r8a7797-pinctrl-Add-pin-function-for-hyperflash.patch \ file://0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch \ + file://0127-IMR-UIO-Driver-initial-version.patch \ + file://0128-rcar_imr-v4l2-driver-Fix-module-support.patch \ " SRC_URI_append_h3ulcb = " file://ulcb.cfg" @@ -178,6 +181,12 @@ KERNEL_DEVICETREE_append_v3hsk = " \ renesas/r8a7798-v3hsk-vbm-v2.dtb \ " +# Prefer V4L2 rcar_imr driver over UIO uio_imr +KERNEL_MODULE_AUTOLOAD += "rcar_imr" +KERNEL_MODULE_PROBECONF += "rcar_imr" +KERNEL_MODULE_PROBECONF += "uio_imr" +module_conf_uio_imr = 'blacklist uio_imr' + # V3H VIP devices KERNEL_MODULE_AUTOLOAD_r8a7798 += "uio_pdrv_genirq" KERNEL_MODULE_PROBECONF_r8a7798 += "uio_pdrv_genirq" -- cgit 1.2.3-korg From 7051d448e4589b74ec69ba56910e4cfbf1c86877 Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Wed, 14 Feb 2018 12:03:23 +0300 Subject: MMNGR: Add cached buffers support. --- .../files/0003-kernelheap-cached-support.patch | 102 +++++++++++++++++++ .../kernel-module-mmngr.bbappend | 5 +- .../files/0001-cached-buffers-support.patch | 113 +++++++++++++++++++++ .../mmngr-module/mmngr-user-module.bbappend | 5 + 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/files/0003-kernelheap-cached-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/files/0001-cached-buffers-support.patch create mode 100644 meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/mmngr-user-module.bbappend diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/files/0003-kernelheap-cached-support.patch b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/files/0003-kernelheap-cached-support.patch new file mode 100644 index 0000000..171eac1 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/files/0003-kernelheap-cached-support.patch @@ -0,0 +1,102 @@ +From d20b6893f3d22abd12ffb5421292fcee4820de33 Mon Sep 17 00:00:00 2001 +From: Andrey Dolnikov +Date: Wed, 14 Feb 2018 17:12:01 +0300 +Subject: [PATCH] mmngr: Add kernelheap cached buffers support. + +--- + .../mmngr/mmngr-module/files/mmngr/drv/mmngr_drv.c | 20 ++++++++++++++++---- + .../files/mmngr/include/mmngr_private_cmn.h | 7 +++++++ + .../files/mmngr/include/mmngr_public_cmn.h | 1 + + 4 files changed, 29 insertions(+), 4 deletions(-) + +diff --git a/mmngr_drv/mmngr/mmngr-module/files/mmngr/drv/mmngr_drv.c b/mmngr_drv/mmngr/mmngr-module/files/mmngr/drv/mmngr_drv.c +index cd797f5..c38329d 100644 +--- a/mmngr_drv/mmngr/mmngr-module/files/mmngr/drv/mmngr_drv.c ++++ b/mmngr_drv/mmngr/mmngr-module/files/mmngr/drv/mmngr_drv.c +@@ -736,7 +736,7 @@ static int close(struct inode *inode, struct file *file) + int entry = 0; + + if (p) { +- if ((p->flag == MM_KERNELHEAP) ++ if ((p->flag == MM_KERNELHEAP || p->flag == MM_KERNELHEAP_CACHED) + && (p->kernel_virt_addr != 0)) { + pr_err("MMD close kernelheap\n"); + mm_dev = mm_drvdata->mm_dev; +@@ -779,6 +779,7 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) + int ercd; + int ret; + struct MM_PARAM *p = file->private_data; ++ struct MM_CACHE_PARAM *cachep; + struct device *mm_dev; + + mm_dev = mm_drvdata->mm_dev; +@@ -830,6 +831,14 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) + goto exit; + } + break; ++ case MM_IOC_FLUSH: ++ cachep = (struct MM_CACHE_PARAM *) arg; ++ dma_sync_single_for_device(mm_dev, p->hard_addr + cachep->offset, cachep->len, DMA_FROM_DEVICE); ++ break; ++ case MM_IOC_INVAL: ++ cachep = (struct MM_CACHE_PARAM *) arg; ++ dma_sync_single_for_cpu(mm_dev, p->hard_addr + cachep->offset, cachep->len, DMA_TO_DEVICE); ++ break; + default: + pr_err("MMD CMD EFAULT\n"); + ret = -EFAULT; +@@ -954,9 +963,12 @@ static int mmap(struct file *filp, struct vm_area_struct *vma) + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + +- vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); +- +- vma->vm_flags |= (VM_IO | VM_DONTEXPAND | VM_DONTDUMP); ++ if (p->flag != MM_KERNELHEAP_CACHED) { ++ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ++ vma->vm_flags |= (VM_IO | VM_DONTEXPAND | VM_DONTDUMP); ++ } else { ++ vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); ++ } + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) +diff --git a/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_private_cmn.h b/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_private_cmn.h +index f731ab6..5f137c6 100644 +--- a/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_private_cmn.h ++++ b/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_private_cmn.h +@@ -72,6 +72,11 @@ struct MM_PARAM { + unsigned int flag; + }; + ++struct MM_CACHE_PARAM { ++ size_t offset; ++ size_t len; ++}; ++ + #define MM_IOC_MAGIC 'm' + #define MM_IOC_ALLOC _IOWR(MM_IOC_MAGIC, 0, struct MM_PARAM) + #define MM_IOC_FREE _IOWR(MM_IOC_MAGIC, 1, struct MM_PARAM) +@@ -80,5 +85,7 @@ struct MM_PARAM { + #define MM_IOC_ALLOC_CO _IOWR(MM_IOC_MAGIC, 4, struct MM_PARAM) + #define MM_IOC_FREE_CO _IOWR(MM_IOC_MAGIC, 5, struct MM_PARAM) + #define MM_IOC_SHARE _IOWR(MM_IOC_MAGIC, 6, struct MM_PARAM) ++#define MM_IOC_FLUSH _IOWR(MM_IOC_MAGIC, 7, struct MM_CACHE_PARAM) ++#define MM_IOC_INVAL _IOWR(MM_IOC_MAGIC, 8, struct MM_CACHE_PARAM) + + #endif /* __MMNGR_PRIVATE_CMN_H__ */ +diff --git a/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_public_cmn.h b/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_public_cmn.h +index dce81a1..a668019 100644 +--- a/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_public_cmn.h ++++ b/mmngr_drv/mmngr/mmngr-module/files/mmngr/include/mmngr_public_cmn.h +@@ -62,6 +62,7 @@ + #define __MMNGR_PUBLIC_CMN_H__ + + #define MM_KERNELHEAP 0 ++#define MM_KERNELHEAP_CACHED 5 + #define MM_CARVEOUT 1 + #define MM_CARVEOUT_SSP 3 + #define MM_CARVEOUT_LOSSY 4 +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/kernel-module-mmngr.bbappend b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/kernel-module-mmngr.bbappend index 6f05e72..bdb0251 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/kernel-module-mmngr.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-mmngr/kernel-module-mmngr.bbappend @@ -6,4 +6,7 @@ MMNGR_CFG_condor = "MMNGR_V3MSK" MMNGR_CFG_v3mzf = "MMNGR_V3MSK" MMNGR_CFG_v3hsk = "MMNGR_V3MSK" -SRC_URI_append = " file://0002-mmngr-Add-V3MSK-board.patch" +SRC_URI_append = " \ + file://0002-mmngr-Add-V3MSK-board.patch \ + file://0003-kernelheap-cached-support.patch \ +" diff --git a/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/files/0001-cached-buffers-support.patch b/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/files/0001-cached-buffers-support.patch new file mode 100644 index 0000000..9a8030e --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/files/0001-cached-buffers-support.patch @@ -0,0 +1,113 @@ +From ac078825fab834a524e45befa6a7f9de12317d16 Mon Sep 17 00:00:00 2001 +From: Andrey Dolnikov +Date: Wed, 14 Feb 2018 12:10:09 +0300 +Subject: [PATCH] mmngr: Add kernelheap cached buffers support. + +--- + libmmngr/mmngr/if/mmngr_if.c | 49 ++++++++++++++++++++++++++++-- + libmmngr/mmngr/include/mmngr_user_public.h | 4 +++ + 2 files changed, 50 insertions(+), 3 deletions(-) + +diff --git a/libmmngr/mmngr/if/mmngr_if.c b/libmmngr/mmngr/if/mmngr_if.c +index 670aa60..1096d14 100644 +--- a/libmmngr/mmngr/if/mmngr_if.c ++++ b/libmmngr/mmngr/if/mmngr_if.c +@@ -216,7 +216,7 @@ int mmngr_alloc_in_user_ext(MMNGR_ID *pid, size_t size, + goto exit; + } + +- if ((flag != MM_KERNELHEAP) && (flag != MM_CARVEOUT) ++ if ((flag != MM_KERNELHEAP) && (flag != MM_KERNELHEAP_CACHED) && (flag != MM_CARVEOUT) + && (flag != MM_CARVEOUT_SSP) && (flag != MM_CARVEOUT_LOSSY)) { + ret = R_MM_PARE; + goto exit; +@@ -243,7 +243,7 @@ int mmngr_alloc_in_user_ext(MMNGR_ID *pid, size_t size, + } + } + +- if (flag == MM_KERNELHEAP) { ++ if (flag == MM_KERNELHEAP || flag == MM_KERNELHEAP_CACHED) { + ret = mm_alloc_kh_in_user(pid, size, phard_addr, + puser_virt_addr, flag); + if (ret) +@@ -296,7 +296,7 @@ int mmngr_free_in_user_ext(MMNGR_ID id) + goto exit; + } + +- if (p.flag == MM_KERNELHEAP) { ++ if (p.flag == MM_KERNELHEAP || p.flag == MM_KERNELHEAP_CACHED) { + ret = mm_free_kh_in_user(id); + if (ret) + goto exit; +@@ -479,3 +479,46 @@ int mmngr_release_in_user(MMNGR_ID id) + ret = mmngr_release_in_user_ext(id); + return ret; + } ++ ++int mmngr_inval(MMNGR_ID id, size_t offset, size_t len) ++{ ++ int ret; ++ struct MM_CACHE_PARAM cachep; ++ ++ cachep.offset = offset; ++ cachep.len = len; ++ ++ ret = ioctl(id, MM_IOC_INVAL, &cachep); ++ if (ret) { ++ perror("MMI INVAL"); ++ ret = R_MM_FATAL; ++ goto exit; ++ } ++ ++ return R_MM_OK; ++ ++exit: ++ return ret; ++ ++} ++ ++int mmngr_flush(MMNGR_ID id, size_t offset, size_t len) ++{ ++ int ret; ++ struct MM_CACHE_PARAM cachep; ++ ++ cachep.offset = offset; ++ cachep.len = len; ++ ++ ret = ioctl(id, MM_IOC_FLUSH, &cachep); ++ if (ret) { ++ perror("MMI INVAL"); ++ ret = R_MM_FATAL; ++ goto exit; ++ } ++ ++ return R_MM_OK; ++ ++exit: ++ return ret; ++} +diff --git a/libmmngr/mmngr/include/mmngr_user_public.h b/libmmngr/mmngr/include/mmngr_user_public.h +index e4e0e2a..9fc0c0a 100644 +--- a/libmmngr/mmngr/include/mmngr_user_public.h ++++ b/libmmngr/mmngr/include/mmngr_user_public.h +@@ -54,6 +54,9 @@ int mmngr_share_in_user(MMNGR_ID *pid, + unsigned long *puser_virt_addr); + int mmngr_release_in_user(MMNGR_ID id); + ++int mmngr_inval(MMNGR_ID id, size_t offset, size_t len); ++int mmngr_flush(MMNGR_ID id, size_t offset, size_t len); ++ + #define R_MM_OK 0 + #define R_MM_FATAL -1 + #define R_MM_SEQE -2 +@@ -61,6 +64,7 @@ int mmngr_release_in_user(MMNGR_ID id); + #define R_MM_NOMEM -4 + + #define MMNGR_VA_SUPPORT MM_KERNELHEAP ++#define MMNGR_VA_SUPPORT_CACHED MM_KERNELHEAP_CACHED + #define MMNGR_PA_SUPPORT MM_CARVEOUT + #define MMNGR_PA_SUPPORT_SSP MM_CARVEOUT_SSP + #define MMNGR_PA_SUPPORT_LOSSY MM_CARVEOUT_LOSSY +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/mmngr-user-module.bbappend b/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/mmngr-user-module.bbappend new file mode 100644 index 0000000..93ac63d --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-multimedia/mmngr-module/mmngr-user-module.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/files:" + +SRC_URI_append = " \ + file://0001-cached-buffers-support.patch;patchdir=${WORKDIR}/git \ +" -- cgit 1.2.3-korg From 620c0dd2ee137ea588326362c6c27dd175a7ed9d Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Wed, 11 Apr 2018 17:15:57 +0300 Subject: linux-renesas: VSP1: Add cropping handling to VSP alpha-planes --- ...Add-cropping-handling-to-VSP-alpha-planes.patch | 44 ++++++++++++++++++++++ .../linux/linux-renesas_4.9.bbappend | 1 + 2 files changed, 45 insertions(+) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0129-Add-cropping-handling-to-VSP-alpha-planes.patch diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0129-Add-cropping-handling-to-VSP-alpha-planes.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0129-Add-cropping-handling-to-VSP-alpha-planes.patch new file mode 100644 index 0000000..d55b196 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0129-Add-cropping-handling-to-VSP-alpha-planes.patch @@ -0,0 +1,44 @@ +From 4b26e8c561541c68c5755660eaad355d9a9afc86 Mon Sep 17 00:00:00 2001 +From: Konstantin Kozhevnikov +Date: Wed, 11 Apr 2018 05:11:21 -0700 +Subject: [PATCH] Add cropping handling to VSP alpha-planes + +--- + drivers/media/platform/vsp1/vsp1_dl.c | 2 +- + drivers/media/platform/vsp1/vsp1_rpf.c | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c +index f9c6d09..d6314cd 100644 +--- a/drivers/media/platform/vsp1/vsp1_dl.c ++++ b/drivers/media/platform/vsp1/vsp1_dl.c +@@ -363,7 +363,7 @@ void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_list *dl, struct vsp1_rwpf *rpf) + dl->src_dst_addr[v_bot_index].addr = v_bot_addr; + + /* ...set alpha-plane address as needed */ +- dl->src_dst_addr[alpha_index].addr = rpf->mem.alpha; ++ dl->src_dst_addr[alpha_index].addr = rpf->mem.alpha + crop->top * width + crop->left; + } + + static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) +diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c +index 2cce294..1f70186 100644 +--- a/drivers/media/platform/vsp1/vsp1_rpf.c ++++ b/drivers/media/platform/vsp1/vsp1_rpf.c +@@ -292,10 +292,11 @@ static void rpf_configure(struct vsp1_entity *entity, + + // ...setup alpha-plane as required + if (rpf->mem.alpha) { +- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_AI, rpf->mem.alpha); ++ struct v4l2_rect *crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); ++ vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_AI, rpf->mem.alpha + crop->top * rpf->alpha_pitch + crop->left); + vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_8B_PLANE); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ASTRIDE, rpf->alpha_pitch); +- dev_dbg(vsp1->dev, "rpf#%d: setup alpha-plane: buffer=%pad, stride=%u\n", rpf->entity.index, &rpf->mem.alpha, rpf->alpha_pitch); ++ dev_dbg(vsp1->dev, "rpf#%d: setup alpha-plane: buffer=%pad, crop=%d,%d, stride=%u\n", rpf->entity.index, &rpf->mem.alpha, crop->left, crop->top, rpf->alpha_pitch); + goto out; + } + +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend index 0233afa..b26c5d4 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend @@ -108,6 +108,7 @@ SRC_URI_append = " \ file://0126-r8a7798-pinctrl-Add-pin-function-for-hyperflash.patch \ file://0127-IMR-UIO-Driver-initial-version.patch \ file://0128-rcar_imr-v4l2-driver-Fix-module-support.patch \ + file://0129-Add-cropping-handling-to-VSP-alpha-planes.patch \ " SRC_URI_append_h3ulcb = " file://ulcb.cfg" -- cgit 1.2.3-korg From 21ea6b4aa00e44223e38e76d32f7407f8b24d908 Mon Sep 17 00:00:00 2001 From: Dmitry Shifrin Date: Fri, 13 Apr 2018 13:40:30 +0300 Subject: Fix r8a7798 build --- meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend index b26c5d4..6c8337b 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend @@ -189,6 +189,6 @@ KERNEL_MODULE_PROBECONF += "uio_imr" module_conf_uio_imr = 'blacklist uio_imr' # V3H VIP devices -KERNEL_MODULE_AUTOLOAD_r8a7798 += "uio_pdrv_genirq" -KERNEL_MODULE_PROBECONF_r8a7798 += "uio_pdrv_genirq" +KERNEL_MODULE_AUTOLOAD_append_r8a7798 += " uio_pdrv_genirq" +KERNEL_MODULE_PROBECONF_append_r8a7798 += " uio_pdrv_genirq" module_conf_uio_pdrv_genirq_r8a7798 = 'options uio_pdrv_genirq of_id="generic-uio"' -- cgit 1.2.3-korg From 20419404d8b8e36eccc19e80995ca2d1c518a5e0 Mon Sep 17 00:00:00 2001 From: Dmitry Shifrin Date: Mon, 16 Apr 2018 15:13:18 +0300 Subject: UBoot: Condor: Add reset support --- .../0019-board-renesas-Add-Condor-board.patch | 45 ++++++++++++++-------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-bsp/u-boot/u-boot/0019-board-renesas-Add-Condor-board.patch b/meta-rcar-gen3-adas/recipes-bsp/u-boot/u-boot/0019-board-renesas-Add-Condor-board.patch index 36644ba..704c4b9 100644 --- a/meta-rcar-gen3-adas/recipes-bsp/u-boot/u-boot/0019-board-renesas-Add-Condor-board.patch +++ b/meta-rcar-gen3-adas/recipes-bsp/u-boot/u-boot/0019-board-renesas-Add-Condor-board.patch @@ -1,4 +1,4 @@ -From 8dba0b32243d57d48cc8b821b9252b0a7f40e5c4 Mon Sep 17 00:00:00 2001 +From 4b72eea699c087004b12f759da98e7c6a3d663fa Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Mon, 22 Jan 2018 13:21:35 +0300 Subject: [PATCH] board: renesas: Add Condor board @@ -6,15 +6,17 @@ Subject: [PATCH] board: renesas: Add Condor board Condor is a board based on R-Car V3H SoC (R8A7798) Signed-off-by: Vladimir Barinov + +%% original patch: 0019-board-renesas-Add-Condor-board.patch --- arch/arm/cpu/armv8/Kconfig | 4 + board/renesas/condor/Kconfig | 15 +++ board/renesas/condor/MAINTAINERS | 6 + board/renesas/condor/Makefile | 10 ++ - board/renesas/condor/condor.c | 264 +++++++++++++++++++++++++++++++++++++++ + board/renesas/condor/condor.c | 272 +++++++++++++++++++++++++++++++++++++++ configs/r8a7798_condor_defconfig | 10 ++ - include/configs/r8a7798_condor.h | 160 ++++++++++++++++++++++++ - 7 files changed, 469 insertions(+) + include/configs/r8a7798_condor.h | 161 +++++++++++++++++++++++ + 7 files changed, 478 insertions(+) create mode 100644 board/renesas/condor/Kconfig create mode 100644 board/renesas/condor/MAINTAINERS create mode 100644 board/renesas/condor/Makefile @@ -94,10 +96,10 @@ index 0000000..d11d859 +obj-y := condor.o ../rcar-gen3-common/common.o diff --git a/board/renesas/condor/condor.c b/board/renesas/condor/condor.c new file mode 100644 -index 0000000..a46a1a7 +index 0000000..dfbfe87 --- /dev/null +++ b/board/renesas/condor/condor.c -@@ -0,0 +1,264 @@ +@@ -0,0 +1,272 @@ +/* + * board/renesas/condor/condor.c + * This is Condor board support. @@ -134,6 +136,7 @@ index 0000000..a46a1a7 +#define RAVB_MSTP812 (1 << 12) +#define RPC_MSTP917 (1 << 17) +#define SD0_MSTP314 (1 << 14) ++#define I2C0_MSTP931 (1 << 31) + +#define SD0CKCR 0xE6150074 + @@ -173,6 +176,8 @@ index 0000000..a46a1a7 +#endif + /* QSPI/RPC */ + mstp_clrbits_le32(MSTPSR9, SMSTPCR9, RPC_MSTP917); ++ /* I2C0 */ ++ mstp_clrbits_le32(MSTPSR9, SMSTPCR9, I2C0_MSTP931); + + freq = rcar_get_sdhi_config_clk(); + writel(freq, SD0CKCR); @@ -353,6 +358,11 @@ index 0000000..a46a1a7 + +void reset_cpu(ulong addr) +{ ++#if defined(CONFIG_SYS_I2C) && defined(CONFIG_SYS_I2C_RCAR) ++ i2c_set_bus_num(0); ++ i2c_init(400000, 0); ++ i2c_reg_write(CONFIG_SYS_I2C_POWERIC_ADDR, 0x20, 0x80); ++#endif +} + +#if defined(CONFIG_DISPLAY_BOARDINFO) @@ -380,10 +390,10 @@ index 0000000..1cab2ae +CONFIG_SPI_FLASH_BAR=y diff --git a/include/configs/r8a7798_condor.h b/include/configs/r8a7798_condor.h new file mode 100644 -index 0000000..a7cc2a0 +index 0000000..6bcc79a --- /dev/null +++ b/include/configs/r8a7798_condor.h -@@ -0,0 +1,160 @@ +@@ -0,0 +1,161 @@ +/* + * include/configs/r8a7798_condor.h + * This file is Condor board configuration. @@ -470,6 +480,8 @@ index 0000000..a7cc2a0 +#define CONFIG_PLL1_CLK_FREQ (CONFIG_SYS_CLK_FREQ * 192 / 2) +#define CONFIG_S3D2_CLK_FREQ (266666666u/2) +#define CONFIG_S3D4_CLK_FREQ (266666666u/4) ++#define CONFIG_S3D2_CLK_FREQ (133333333u) ++ + +/* Generic Timer Definitions (use in assembler source) */ +#define COUNTER_FREQUENCY 0xFE502A /* 16.66MHz from CPclk */ @@ -481,14 +493,13 @@ index 0000000..a7cc2a0 + +/* i2c */ +#define CONFIG_SYS_I2C -+#define CONFIG_SYS_I2C_SH -+#define CONFIG_SYS_I2C_SLAVE 0x60 -+#define CONFIG_SYS_I2C_SH_NUM_CONTROLLERS 1 -+#define CONFIG_SYS_I2C_SH_SPEED0 400000 -+#define CONFIG_SH_I2C_DATA_HIGH 4 -+#define CONFIG_SH_I2C_DATA_LOW 5 -+#define CONFIG_SH_I2C_CLOCK 10000000 -+ ++#define CONFIG_SYS_I2C_RCAR ++#define CONFIG_SYS_RCAR_I2C0_SPEED 400000 ++#define CONFIG_SYS_RCAR_I2C1_SPEED 400000 ++#define CONFIG_SYS_RCAR_I2C2_SPEED 400000 ++#define CONFIG_SYS_RCAR_I2C3_SPEED 400000 ++#define CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS 4 ++#define CONFIG_HP_CLK_FREQ CONFIG_S3D2_CLK_FREQ +#define CONFIG_SYS_I2C_POWERIC_ADDR 0x30 + +/* USB */ @@ -545,5 +556,5 @@ index 0000000..a7cc2a0 + +#endif /* __CONDOR_H */ -- -1.9.1 +2.7.4 -- cgit 1.2.3-korg From 19455db9f65c51565ac8af8ceecf627e88fdd5f7 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Wed, 11 Apr 2018 19:09:47 +0300 Subject: V3M: add ISP resources in dtsi --- ...as-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch index 1f2b11c..0b67bc1 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch @@ -8,7 +8,7 @@ This adds Renesas R8A7797 SoC support Signed-off-by: Vladimir Barinov --- arch/arm64/Kconfig.platforms | 8 + - arch/arm64/boot/dts/renesas/r8a7797.dtsi | 1156 +++++++++++ + arch/arm64/boot/dts/renesas/r8a7797.dtsi | 1168 +++++++++++ drivers/clk/renesas/Kconfig | 1 + drivers/clk/renesas/Makefile | 1 + drivers/clk/renesas/r8a7797-cpg-mssr.c | 232 +++ @@ -47,7 +47,7 @@ Signed-off-by: Vladimir Barinov drivers/thermal/rcar_gen3_thermal.c | 29 + include/dt-bindings/clock/r8a7797-cpg-mssr.h | 48 + include/dt-bindings/power/r8a7797-sysc.h | 32 + - 40 files changed, 4489 insertions(+), 29 deletions(-) + 40 files changed, 4501 insertions(+), 29 deletions(-) create mode 100644 arch/arm64/boot/dts/renesas/r8a7797.dtsi create mode 100644 drivers/clk/renesas/r8a7797-cpg-mssr.c create mode 100644 drivers/pinctrl/sh-pfc/pfc-r8a7797.c @@ -76,10 +76,10 @@ index ebe0a37..9cebaad 100644 help diff --git a/arch/arm64/boot/dts/renesas/r8a7797.dtsi b/arch/arm64/boot/dts/renesas/r8a7797.dtsi new file mode 100644 -index 0000000..5319b1a +index 0000000..faefe8a --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a7797.dtsi -@@ -0,0 +1,1156 @@ +@@ -0,0 +1,1167 @@ +/* + * Device Tree Source for the r8a7797 SoC + * @@ -115,6 +115,7 @@ index 0000000..5319b1a + vin2 = &vin2; + vin3 = &vin3; + tsc0 = &tsc1; ++ isp0 = &isp0; + }; + + psci { @@ -1234,6 +1235,16 @@ index 0000000..5319b1a + clocks = <&cpg CPG_MOD 820>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; + }; ++ ++ isp0: isp@fec00000 { ++ compatible = "renesas,isp-r8a7797"; ++ reg = <0 0xfec00000 0 0x20000>, ++ <0 0xfed00000 0 0x4000>; ++ interrupts = , ++ ; ++ clocks = <&cpg CPG_MOD 817>; ++ power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; ++ }; + }; +}; diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig @@ -1821,10 +1832,10 @@ index ecae864..d5fa06c 100644 lvdcr0 |= LVDCR0_LVRES; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c -index 74c17d8..149c107 100644 +index 156fe5f..ea451cd 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c -@@ -807,6 +807,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) +@@ -836,6 +836,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) { .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,i2c-r8a77965", .data = (void *)I2C_RCAR_GEN3 }, @@ -5171,7 +5182,7 @@ index 1b33c50..63f943d 100644 { .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 }, #endif diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c -index 36e70db..a2606fe 100644 +index 2d3ac0b..1488964 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -216,7 +216,8 @@ static int msiof_rcar_is_gen3(struct device *dev) -- cgit 1.2.3-korg From 1f30c03ce70466b2e48cc0ace8cb8ce7d3fa1f2c Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Wed, 11 Apr 2018 19:10:10 +0300 Subject: V3H: add ISP resources in dtsi --- ...as-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch | 40 +++++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch index 7d57c38..321258f 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch @@ -9,7 +9,7 @@ Signed-off-by: Vladimir Barinov Signed-off-by: Mikhail Ulyanov --- arch/arm64/Kconfig.platforms | 8 + - arch/arm64/boot/dts/renesas/r8a7798.dtsi | 1700 +++++++++++++ + arch/arm64/boot/dts/renesas/r8a7798.dtsi | 1724 ++++++++++++++ drivers/clk/renesas/Kconfig | 1 + drivers/clk/renesas/Makefile | 1 + drivers/clk/renesas/r8a7798-cpg-mssr.c | 292 +++ @@ -47,7 +47,7 @@ Signed-off-by: Mikhail Ulyanov drivers/thermal/rcar_gen3_thermal.c | 10 + include/dt-bindings/clock/r8a7798-cpg-mssr.h | 56 + include/dt-bindings/power/r8a7798-sysc.h | 46 + - 39 files changed, 5618 insertions(+), 33 deletions(-) + 39 files changed, 5642 insertions(+), 33 deletions(-) create mode 100644 arch/arm64/boot/dts/renesas/r8a7798.dtsi create mode 100644 drivers/clk/renesas/r8a7798-cpg-mssr.c create mode 100644 drivers/pinctrl/sh-pfc/pfc-r8a7798.c @@ -76,10 +76,10 @@ index 9cebaad..3646b6e 100644 help diff --git a/arch/arm64/boot/dts/renesas/r8a7798.dtsi b/arch/arm64/boot/dts/renesas/r8a7798.dtsi new file mode 100644 -index 0000000..6412a24 +index 0000000..00bd4d6 --- /dev/null +++ b/arch/arm64/boot/dts/renesas/r8a7798.dtsi -@@ -0,0 +1,1700 @@ +@@ -0,0 +1,1722 @@ +/* + * Device Tree Source for the r8a7798 SoC + * @@ -131,6 +131,8 @@ index 0000000..6412a24 + vin15 = &vin15; + tsc0 = &tsc1; + tsc1 = &tsc2; ++ isp0 = &isp0; ++ isp1 = &isp1; + }; + + psci { @@ -1778,6 +1780,26 @@ index 0000000..6412a24 + clocks = <&cpg CPG_MOD 1000>; + power-domains = <&sysc R8A7798_PD_A3VIP2>; + }; ++ ++ isp0: isp@fec00000 { ++ compatible = "renesas,isp-r8a7798"; ++ reg = <0 0xfec00000 0 0x20000>, ++ <0 0xfed00000 0 0x4000>; ++ interrupts = , ++ ; ++ clocks = <&cpg CPG_MOD 817>; ++ power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ }; ++ ++ isp1: isp@fee00000 { ++ compatible = "renesas,isp-r8a7798"; ++ reg = <0 0xfee00000 0 0x20000>, ++ <0 0xfed20000 0 0x4000>; ++ interrupts = , ++ ; ++ clocks = <&cpg CPG_MOD 814>; ++ power-domains = <&sysc R8A7798_PD_ALWAYS_ON>; ++ }; + }; +}; diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig @@ -2253,10 +2275,10 @@ index 3916b63..22c7713 100644 rgrp->dptsr_planes); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c -index 149c107..0ad583a 100644 +index ea451cd..6528f72 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c -@@ -808,6 +808,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) +@@ -837,6 +837,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) { .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,i2c-r8a77965", .data = (void *)I2C_RCAR_GEN3 }, { .compatible = "renesas,i2c-r8a7797", .data = (void *)I2C_RCAR_GEN3 }, @@ -2279,7 +2301,7 @@ index 1ae9174..41e14fa 100644 }, }; diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig -index 5539c5d..fc7d829 100644 +index d60909a..4ed8009 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -39,7 +39,7 @@ config VIDEO_RCAR_VIN_LEGACY_DEBUG @@ -2292,7 +2314,7 @@ index 5539c5d..fc7d829 100644 This is a v4l2 driver for the R-Car CSI-2 Interface diff --git a/drivers/media/platform/soc_camera/rcar_csi2.c b/drivers/media/platform/soc_camera/rcar_csi2.c -index 2ef27e8..98f271f 100644 +index 53fc644..20ffba1 100644 --- a/drivers/media/platform/soc_camera/rcar_csi2.c +++ b/drivers/media/platform/soc_camera/rcar_csi2.c @@ -163,6 +163,11 @@ @@ -6317,7 +6339,7 @@ index 63f943d..b1fcae1 100644 { .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 }, #endif diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c -index a2606fe..13fd706 100644 +index 1488964..9ae47e5 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -217,7 +217,8 @@ static int msiof_rcar_is_gen3(struct device *dev) -- cgit 1.2.3-korg From a6131f564f4a5d7da72fe18d327c28fd2e4f234a Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Wed, 11 Apr 2018 19:11:28 +0300 Subject: LVDS Sensors: add OX03A initial, IMX390, fix OV2775 This adds sensor OX03A, IMX390 Fix pll for OV2775 Fix refclk for all TI953 users Add ADV_DEBUG for soc_platform drivers --- .../linux-renesas/0030-Gen3-LVDS-cameras.patch | 11966 +++++++++++++++---- 1 file changed, 9364 insertions(+), 2602 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0030-Gen3-LVDS-cameras.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0030-Gen3-LVDS-cameras.patch index fe71743..1623d12 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0030-Gen3-LVDS-cameras.patch +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0030-Gen3-LVDS-cameras.patch @@ -6,45 +6,51 @@ Subject: [PATCH] Gen3: LVDS cameras This add Gen3 LVDS cameras support: - deserializers: MAX9286, DS90UB954/960/964 - cameras: ov10635, ov490+ov10640, ov495+OV2775, ar0132, ar0220, - ap0101+ar014x, ov2775 + ap0101+ar014x, ov2775, imx390, ox03a Signed-off-by: Vladimir Barinov --- drivers/media/i2c/soc_camera/Kconfig | 19 + drivers/media/i2c/soc_camera/Makefile | 3 + - drivers/media/i2c/soc_camera/ap0101_ar014x.c | 588 +++++++ + drivers/media/i2c/soc_camera/ap0101_ar014x.c | 588 ++++ drivers/media/i2c/soc_camera/ap0101_ar014x.h | 28 + - drivers/media/i2c/soc_camera/ar0132.c | 565 +++++++ - drivers/media/i2c/soc_camera/ar0132.h | 213 +++ - drivers/media/i2c/soc_camera/ar0220.c | 528 +++++++ - drivers/media/i2c/soc_camera/ar0220.h | 309 ++++ - drivers/media/i2c/soc_camera/max9286.c | 692 ++++++++ - drivers/media/i2c/soc_camera/max9286.h | 244 +++ - drivers/media/i2c/soc_camera/ov10635.c | 759 +++++++++ - drivers/media/i2c/soc_camera/ov10635.h | 1139 +++++++++++++ + drivers/media/i2c/soc_camera/ar0132.c | 565 ++++ + drivers/media/i2c/soc_camera/ar0132.h | 212 ++ + drivers/media/i2c/soc_camera/ar0220.c | 538 +++ + drivers/media/i2c/soc_camera/ar0220.h | 43 + + drivers/media/i2c/soc_camera/imx390.c | 533 +++ + drivers/media/i2c/soc_camera/imx390.h | 3817 ++++++++++++++++++++++ + drivers/media/i2c/soc_camera/max9286.c | 692 ++++ + drivers/media/i2c/soc_camera/max9286.h | 244 ++ + drivers/media/i2c/soc_camera/ov10635.c | 759 +++++ + drivers/media/i2c/soc_camera/ov10635.h | 1139 +++++++ drivers/media/i2c/soc_camera/ov10635_debug.h | 54 + - drivers/media/i2c/soc_camera/ov106xx.c | 139 ++ - drivers/media/i2c/soc_camera/ov2775.c | 527 +++++++ - drivers/media/i2c/soc_camera/ov2775.h | 1841 ++++++++++++++++++++++ - drivers/media/i2c/soc_camera/ov490_ov10640.c | 1133 +++++++++++++ - drivers/media/i2c/soc_camera/ov490_ov10640.h | 102 ++ - drivers/media/i2c/soc_camera/ov495_ov2775.c | 639 ++++++++ + drivers/media/i2c/soc_camera/ov106xx.c | 161 + + drivers/media/i2c/soc_camera/ov2775.c | 538 +++ + drivers/media/i2c/soc_camera/ov2775.h | 1841 +++++++++++ + drivers/media/i2c/soc_camera/ov490_ov10640.c | 1133 +++++++ + drivers/media/i2c/soc_camera/ov490_ov10640.h | 102 + + drivers/media/i2c/soc_camera/ov495_ov2775.c | 650 ++++ drivers/media/i2c/soc_camera/ov495_ov2775.h | 23 + - drivers/media/i2c/soc_camera/ti9x4.c | 518 ++++++ - drivers/media/i2c/soc_camera/ti9x4.h | 156 ++ - drivers/media/platform/soc_camera/rcar_csi2.c | 297 ++-- - drivers/media/platform/soc_camera/rcar_vin.c | 194 ++- - drivers/media/platform/soc_camera/soc_camera.c | 17 +- - drivers/media/platform/soc_camera/soc_mediabus.c | 16 + + drivers/media/i2c/soc_camera/ox03a.c | 526 +++ + drivers/media/i2c/soc_camera/ox03a.h | 1724 ++++++++++ + drivers/media/i2c/soc_camera/ti9x4.c | 518 +++ + drivers/media/i2c/soc_camera/ti9x4.h | 156 + + drivers/media/platform/soc_camera/rcar_csi2.c | 312 +- + drivers/media/platform/soc_camera/rcar_vin.c | 194 +- + drivers/media/platform/soc_camera/soc_camera.c | 47 +- + drivers/media/platform/soc_camera/soc_mediabus.c | 26 + include/media/drv-intf/soc_mediabus.h | 3 + - include/media/soc_camera.h | 1 + - 28 files changed, 10638 insertions(+), 109 deletions(-) + include/media/soc_camera.h | 5 + + 32 files changed, 17066 insertions(+), 127 deletions(-) create mode 100644 drivers/media/i2c/soc_camera/ap0101_ar014x.c create mode 100644 drivers/media/i2c/soc_camera/ap0101_ar014x.h create mode 100644 drivers/media/i2c/soc_camera/ar0132.c create mode 100644 drivers/media/i2c/soc_camera/ar0132.h create mode 100644 drivers/media/i2c/soc_camera/ar0220.c create mode 100644 drivers/media/i2c/soc_camera/ar0220.h + create mode 100644 drivers/media/i2c/soc_camera/imx390.c + create mode 100644 drivers/media/i2c/soc_camera/imx390.h create mode 100644 drivers/media/i2c/soc_camera/max9286.c create mode 100644 drivers/media/i2c/soc_camera/max9286.h create mode 100644 drivers/media/i2c/soc_camera/ov10635.c @@ -57,6 +63,8 @@ Signed-off-by: Vladimir Barinov create mode 100644 drivers/media/i2c/soc_camera/ov490_ov10640.h create mode 100644 drivers/media/i2c/soc_camera/ov495_ov2775.c create mode 100644 drivers/media/i2c/soc_camera/ov495_ov2775.h + create mode 100644 drivers/media/i2c/soc_camera/ox03a.c + create mode 100644 drivers/media/i2c/soc_camera/ox03a.h create mode 100644 drivers/media/i2c/soc_camera/ti9x4.c create mode 100644 drivers/media/i2c/soc_camera/ti9x4.h @@ -736,7 +744,7 @@ index 0000000..16599a1 +}; diff --git a/drivers/media/i2c/soc_camera/ar0132.c b/drivers/media/i2c/soc_camera/ar0132.c new file mode 100644 -index 0000000..e124e6a +index 0000000..c0ac733 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ar0132.c @@ -0,0 +1,565 @@ @@ -770,7 +778,7 @@ index 0000000..e124e6a +#define AR0132_PID 0x3000 +#define AR0132_VERSION_REG 0x2400 + -+#define AR0132_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR12_1X12 ++#define AR0132_MEDIA_BUS_FMT MEDIA_BUS_FMT_SGRBG12_1X12 + +struct ar0132_priv { + struct v4l2_subdev sd; @@ -1307,10 +1315,10 @@ index 0000000..e124e6a +#endif diff --git a/drivers/media/i2c/soc_camera/ar0132.h b/drivers/media/i2c/soc_camera/ar0132.h new file mode 100644 -index 0000000..bafa193 +index 0000000..7dfc4e3 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ar0132.h -@@ -0,0 +1,213 @@ +@@ -0,0 +1,212 @@ +/* + * ON Semiconductor AR0132 sensor camera wizard 1110x620@30/BGGR/BT601/12bit + * @@ -1327,16 +1335,15 @@ index 0000000..bafa193 + +#define AR0132_EMBEDDED_LINE + -+#define AR0132_MAX_WIDTH 1665 // (1110*3/2) ++#define AR0132_MAX_WIDTH 1104 +#define AR0132_MAX_HEIGHT 624 + +#define AR0132_DELAY 0xffff + +#define AR0132_MAX_ROI_DIM_X 1288 +#define AR0132_MAX_ROI_DIM_Y 968 -+#define AR0132_InfoLines 4 + -+#define AR0132_ROI_DIM_X 1110 // 1104 ++#define AR0132_ROI_DIM_X 1104 +#define AR0132_ROI_DIM_Y 620 // AR0132_MAX_HEIGHT + +#define AR0132_ROI_Y_START 0x00AE @@ -1370,11 +1377,11 @@ index 0000000..bafa193 + //256: Walking 1 test pattern (12 bit) +#ifdef AR0132_DISPLAY_PATTERN_FIXED +{0x3070, 0x0001}, ++#endif +{0x3072, 0x0123}, // R +{0x3074, 0x0456}, // G(GR row) +{0x3076, 0x0abc}, // B +{0x3078, 0x0def}, // G(GB row) -+#endif +#ifdef AR0132_DISPLAY_PATTERN_COLOR_BAR +{0x3070, 0x0002}, +#endif @@ -1526,10 +1533,10 @@ index 0000000..bafa193 +}; diff --git a/drivers/media/i2c/soc_camera/ar0220.c b/drivers/media/i2c/soc_camera/ar0220.c new file mode 100644 -index 0000000..ef2eb51 +index 0000000..eb20187 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ar0220.c -@@ -0,0 +1,528 @@ +@@ -0,0 +1,538 @@ +/* + * ON Semiconductor AR0220 sensor camera driver + * @@ -1560,7 +1567,7 @@ index 0000000..ef2eb51 +#define AR0220_PID 0x3000 +#define AR0220_VERSION_REG 0x0C54 + -+#define AR0220_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR8_1X8 ++#define AR0220_MEDIA_BUS_FMT MEDIA_BUS_FMT_SGRBG14_1X14 + +struct ar0220_priv { + struct v4l2_subdev sd; @@ -1578,7 +1585,6 @@ index 0000000..ef2eb51 + int port; + int gpio_resetb; + int gpio_fsin; -+ +}; + +static inline struct ar0220_priv *to_ar0220(const struct i2c_client *client) @@ -1850,6 +1856,7 @@ index 0000000..ef2eb51 + u16 val = 0; + u16 pid = 0; + int ret = 0; ++ int tmp_addr; + + /* check and show model ID */ + reg16_read16(client, AR0220_PID, &pid); @@ -1860,6 +1867,16 @@ index 0000000..ef2eb51 + goto err; + } + ++ /* setup XCLK */ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ /* CLK_OUT=22.5792*160*M/N/CLKDIV -> CLK_OUT=27MHz: CLKDIV=2, M=15, N=251: 22.5792*160/8*15/251=26.987MHz=CLK_OUT */ ++ client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++ reg8_write(client, 0x06, 0x6f); /* Set CLKDIV and M */ ++ reg8_write(client, 0x07, 0xfb); /* Set N */ ++ } ++ client->addr = tmp_addr; ++ + /* Program wizard registers */ + ar0220_set_regs(client, ar0220_regs_wizard, ARRAY_SIZE(ar0220_regs_wizard)); + @@ -2060,7 +2077,7 @@ index 0000000..ef2eb51 +#endif diff --git a/drivers/media/i2c/soc_camera/ar0220.h b/drivers/media/i2c/soc_camera/ar0220.h new file mode 100644 -index 0000000..74bfdd2 +index 0000000..205c351 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ar0220.h @@ -0,0 +1,43 @@ @@ -2078,7 +2095,7 @@ index 0000000..74bfdd2 +//#define AR0220_DISPLAY_PATTERN_FIXED +//#define AR0220_DISPLAY_PATTERN_COLOR_BAR + -+#define AR0220_MAX_WIDTH 3648 // (1820*2=3640) <- must be multiple of 16 - requred by R-CAR VIN ++#define AR0220_MAX_WIDTH 1820 +#define AR0220_MAX_HEIGHT 944 + +#define AR0220_DELAY 0xffff @@ -2107,16 +2124,16 @@ index 0000000..74bfdd2 +#endif +{AR0220_DELAY, 100}, // Wait 100ms +}; -diff --git a/drivers/media/i2c/soc_camera/max9286.c b/drivers/media/i2c/soc_camera/max9286.c +diff --git a/drivers/media/i2c/soc_camera/imx390.c b/drivers/media/i2c/soc_camera/imx390.c new file mode 100644 -index 0000000..c850196 +index 0000000..61f430d --- /dev/null -+++ b/drivers/media/i2c/soc_camera/max9286.c -@@ -0,0 +1,692 @@ ++++ b/drivers/media/i2c/soc_camera/imx390.c +@@ -0,0 +1,533 @@ +/* -+ * MAXIM max9286 GMSL driver ++ * OmniVision IMX390 sensor camera driver + * -+ * Copyright (C) 2015-2018 Cogent Embedded, Inc. ++ * Copyright (C) 2018 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -2125,384 +2142,213 @@ index 0000000..c850196 + */ + +#include ++#include +#include +#include -+#include -+#include +#include + ++#include +#include -+#include ++#include +#include -+#include + -+#include "max9286.h" ++#include "imx390.h" + -+#define MAXIM_I2C_I2C_SPEED_837KHZ (0x7 << 2) /* 837kbps */ -+#define MAXIM_I2C_I2C_SPEED_533KHZ (0x6 << 2) /* 533kbps */ -+#define MAXIM_I2C_I2C_SPEED_339KHZ (0x5 << 2) /* 339 kbps */ -+#define MAXIM_I2C_I2C_SPEED_173KHZ (0x4 << 2) /* 174kbps */ -+#define MAXIM_I2C_I2C_SPEED_105KHZ (0x3 << 2) /* 105 kbps */ -+#define MAXIM_I2C_I2C_SPEED_085KHZ (0x2 << 2) /* 84.7 kbps */ -+#define MAXIM_I2C_I2C_SPEED_028KHZ (0x1 << 2) /* 28.3 kbps */ -+#define MAXIM_I2C_I2C_SPEED MAXIM_I2C_I2C_SPEED_339KHZ ++#define IMX390_I2C_ADDR 0x21 + -+struct max9286_priv { -+ struct v4l2_subdev sd[4]; -+ struct device_node *sd_of_node[4]; -+ int des_addr; -+ int des_quirk_addr; /* second MAX9286 on the same I2C bus */ -+ int links; -+ int links_mask; -+ int lanes; -+ int csi_rate; -+ const char *fsync_mode; -+ int fsync_period; -+ char pclk_rising_edge; -+ int gpio_resetb; -+ int active_low_resetb; -+ int him; -+ int hsync; -+ int vsync; -+ int timeout; -+ int poc_delay; -+ atomic_t use_count; -+ u32 csi2_outord; -+ struct i2c_client *client; -+ int max9271_addr_map[4]; -+ int ser_id; -+ struct gpio_desc *poc_gpio[4]; /* PoC power supply */ -+}; ++#define IMX390_PID 0x0330 ++#define IMX390_VER 0x0330 ++#define IMX390_VERSION_REG 0x1515 + -+static char fsync_mode_default[20] = "manual"; /* manual, automatic, semi-automatic, external */ ++#define IMX390_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB12_1X12 + -+static int conf_link; -+module_param(conf_link, int, 0644); -+MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); ++struct imx390_priv { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct media_pad pad; ++ struct v4l2_rect rect; ++ int init_complete; ++ u8 id[6]; ++ int exposure; ++ int gain; ++ int autogain; ++ /* serializers */ ++ int ti9x4_addr; ++ int ti9x3_addr; ++ int port; ++ int gpio_resetb; ++ int gpio_fsin; ++}; + -+static int poc_trig; -+module_param(poc_trig, int, 0644); -+MODULE_PARM_DESC(poc_trig, " Use PoC triggering during reverse channel setup. Useful on systems with dedicated PoC and unstable ser-des lock"); ++static inline struct imx390_priv *to_imx390(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct imx390_priv, sd); ++} + -+static int him; -+module_param(him, int, 0644); -+MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)"); ++static int imx390_set_regs(struct i2c_client *client, ++ const struct imx390_reg *regs, int nr_regs) ++{ ++ int i; + -+static int fsync_period; -+module_param(fsync_period, int, 0644); -+MODULE_PARM_DESC(fsync_period, " Frame sync period (default: 3.2MHz)"); ++ for (i = 0; i < nr_regs; i++) { ++ if (regs[i].reg == IMX390_DELAY) { ++ mdelay(regs[i].val); ++ continue; ++ } + -+static int hsync; -+module_param(hsync, int, 0644); -+MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)"); ++ reg16_write(client, regs[i].reg, regs[i].val); ++ } + -+static int vsync = 1; -+module_param(vsync, int, 0644); -+MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)"); ++ return 0; ++} + -+static int gpio_resetb; -+module_param(gpio_resetb, int, 0644); -+MODULE_PARM_DESC(gpio_resetb, " Serializer GPIO reset (default: 0 - not used)"); ++static int imx390_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} + -+static int active_low_resetb; -+module_param(active_low_resetb, int, 0644); -+MODULE_PARM_DESC(active_low_resetb, " Serializer GPIO reset level (default: 0 - active high)"); ++static int imx390_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); + -+static int poc_delay; -+module_param(poc_delay, int, 0644); -+MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 0 ms)"); ++ if (format->pad) ++ return -EINVAL; + -+static char* ser_name(int id) -+{ -+ switch (id) { -+ case MAX9271_ID: -+ return "MAX9271"; -+ case MAX96705_ID: -+ return "MAX96705"; -+ default: -+ return "unknown"; -+ } ++ mf->width = priv->rect.width; ++ mf->height = priv->rect.height; ++ mf->code = IMX390_MEDIA_BUS_FMT; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; +} + -+static void max9286_preinit(struct i2c_client *client, int addr) ++static int imx390_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); ++ struct v4l2_mbus_framefmt *mf = &format->format; + -+ client->addr = addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x0a, 0x00); /* disable reverse control for all cams */ -+ reg8_write(client, 0x00, 0x00); /* disable all GMSL links [0:3] */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ -+ reg8_write(client, 0x1c, priv->him ? 0xf4 : 0x04); /* high-immunity or legacy mode */ ++ mf->code = IMX390_MEDIA_BUS_FMT; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) ++ cfg->try_fmt = *mf; ++ ++ return 0; +} + -+static void max9286_sensor_reset(struct i2c_client *client, int addr, int reset_on) ++static int imx390_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); ++ if (code->pad || code->index > 0) ++ return -EINVAL; + -+ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) -+ return; ++ code->code = IMX390_MEDIA_BUS_FMT; + -+ /* sensor reset/unreset */ -+ client->addr = addr; /* MAX9271-CAMx I2C */ -+ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | /* set GPIOn value to reset/unreset */ -+ ((priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0) ^ reset_on)); -+ reg8_write(client, 0x0e, 0x42 | BIT(priv->gpio_resetb)); /* set GPIOn direction output */ ++ return 0; +} + -+static void max9286_postinit(struct i2c_client *client, int addr) ++static int imx390_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ int idx; -+ -+ for (idx = 0; idx < priv->links; idx++) { -+ client->addr = priv->des_addr; /* MAX9286 I2C */ -+ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); + -+ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */ -+ max9286_sensor_reset(client, client->addr, 0); /* sensor unreset */ -+ } ++ memcpy(edid->edid, priv->id, 6); + -+ client->addr = addr; /* MAX9286 I2C */ -+ reg8_write(client, 0x0a, 0x00); /* disable reverse control for all cams */ -+ reg8_write(client, 0x00, 0xe0 | priv->links_mask); /* enable GMSL link for CAMs */ -+ reg8_write(client, 0x0b, priv->csi2_outord); /* CSI2 output order */ -+ reg8_write(client, 0x15, 0x9b); /* enable CSI output, VC is set accordingly to Link number, BIT7 magic must be set */ -+ reg8_write(client, 0x1b, priv->links_mask); /* enable equalizer for CAMs */ -+ usleep_range(5000, 5500); /* wait 2ms after any change of reverse channel settings */ ++ edid->edid[6] = 0xff; ++ edid->edid[7] = client->addr; ++ edid->edid[8] = IMX390_VERSION_REG >> 8; ++ edid->edid[9] = IMX390_VERSION_REG & 0xff; + -+ if (strcmp(priv->fsync_mode, "manual") == 0) { -+ reg8_write(client, 0x01, 0x00); /* manual: FRAMESYNC set manually via [0x06:0x08] regs */ -+ } else if (strcmp(priv->fsync_mode, "automatic") == 0) { -+ reg8_write(client, 0x01, 0x02); /* automatic: FRAMESYNC taken from the slowest Link */ -+ } else if (strcmp(priv->fsync_mode, "semi-automatic") == 0) { -+ reg8_write(client, 0x01, 0x01); /* semi-automatic: FRAMESYNC taken from the slowest Link */ -+ } else if (strcmp(priv->fsync_mode, "external") == 0) { -+ reg8_write(client, 0x01, 0xc0); /* ECU (aka MCU) based FrameSync using GPI-to-GPO */ -+ } ++ return 0; +} + -+static int max9286_reverse_channel_setup(struct i2c_client *client, int idx) ++static int imx390_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ u8 val = 0; -+ int timeout = priv->timeout; -+ char timeout_str[10]; -+ int ret = 0; ++ struct v4l2_rect *rect = &sel->r; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); + -+ /* Reverse channel enable */ -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x3f, 0x4f); /* enable custom reverse channel & first pulse length */ -+ reg8_write(client, 0x34, 0xa2 | MAXIM_I2C_I2C_SPEED); /* enable artificial ACKs, I2C speed set */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ -+ reg8_write(client, 0x00, 0xe0 | BIT(idx)); /* enable GMSL link for CAMx */ -+ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse control for CAMx */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || ++ sel->target != V4L2_SEL_TGT_CROP) ++ return -EINVAL; + -+ for (;;) { -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x3b, 0x1e); /* first pulse length rise time changed from 300ns to 200ns, amplitude 100mV */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ rect->left = ALIGN(rect->left, 2); ++ rect->top = ALIGN(rect->top, 2); ++ rect->width = ALIGN(rect->width, 2); ++ rect->height = ALIGN(rect->height, 2); + -+ client->addr = 0x40; /* MAX9271-CAMx I2C */ -+ reg8_write(client, 0x04, 0x43); /* wake-up, enable reverse_control/conf_link */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ -+ reg8_write(client, 0x08, 0x01); /* reverse channel receiver high threshold enable */ -+ reg8_write(client, 0x97, priv->him ? 0xaf : 0x5f); /* enable reverse control channel programming (MAX96705-MAX96711 only) */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ if ((rect->left + rect->width > IMX390_MAX_WIDTH) || ++ (rect->top + rect->height > IMX390_MAX_HEIGHT)) ++ *rect = priv->rect; + -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x3b, 0x19); /* reverse channel increase amplitude 170mV to compensate high threshold enabled */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ priv->rect.left = rect->left; ++ priv->rect.top = rect->top; ++ priv->rect.width = rect->width; ++ priv->rect.height = rect->height; + -+ client->addr = 0x40; /* MAX9271-CAMx I2C */ -+ reg8_read(client, 0x1e, &val); /* read max9271 ID */ -+ if (val == MAX9271_ID || val == MAX96705_ID || --timeout == 0) { -+ priv->ser_id = val; -+ break; -+ } ++ return 0; ++} + -+ /* Check if already initialized (after reboot/reset ?) */ -+ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */ -+ reg8_read(client, 0x1e, &val); /* read max9271 ID */ -+ if (val == MAX9271_ID || val == MAX96705_ID) { -+ priv->ser_id = val; -+ reg8_write(client, 0x04, 0x43); /* enable reverse_control/conf_link */ -+ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ -+ ret = -EADDRINUSE; -+ break; -+ } -+ -+ if (timeout == priv->timeout / 2 && poc_trig) { -+ if (!IS_ERR(priv->poc_gpio[idx])) { -+ gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */ -+ mdelay(200); -+ gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */ -+ mdelay(priv->poc_delay); -+ } -+ } -+ } -+ -+ max9286_sensor_reset(client, client->addr, 1); /* sensor reset */ -+ -+ if (!timeout) { -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ priv->links_mask |= BIT(idx); -+ priv->csi2_outord &= ~(0x3 << (idx * 2)); -+ priv->csi2_outord |= ((hweight8(priv->links_mask) - 1) << (idx * 2)); -+ -+out: -+ sprintf(timeout_str, "retries=%d", priv->timeout - timeout); -+ dev_info(&client->dev, "link%d %s %sat 0x%x %s %s\n", idx, ser_name(priv->ser_id), -+ ret == -EADDRINUSE ? "already " : "", priv->max9271_addr_map[idx], -+ ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "", -+ priv->timeout - timeout? timeout_str : ""); -+ -+ return ret; -+} -+ -+static void max9286_initial_setup(struct i2c_client *client) -+{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ -+ /* Initial setup */ -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x15, 0x13); /* disable CSI output, VC is set accordingly to Link number */ -+ reg8_write(client, 0x69, 0x0f); /* mask CSI forwarding from all links */ -+ switch (priv->lanes) { -+ case 1: -+ reg8_write(client, 0x12, 0x33); /* enable CSI-2 Lane D0, DBL mode, YUV422 8-bit*/ -+ break; -+ case 2: -+ reg8_write(client, 0x12, 0x73); /* enable CSI-2 Lanes D0,D1, DBL mode, YUV422 8-bit*/ -+ break; -+ case 3: -+ reg8_write(client, 0x12, 0xd3); /* enable CSI-2 Lanes D0-D2, DBL mode, YUV422 8-bit*/ -+ break; -+ case 4: -+ reg8_write(client, 0x12, 0xf3); /* enable CSI-2 Lanes D0-D3, DBL mode, YUV422 8-bit*/ -+ break; -+ default: -+ dev_err(&client->dev, "CSI2 lanes number is invalid (%d)\n", priv->lanes); -+ } -+ -+ /* Start GMSL initialization with FSYNC disabled. This is required for some odd LVDS cameras */ -+ reg8_write(client, 0x01, 0xc0); /* ECU (aka MCU) based FrameSync using GPI-to-GPO */ -+ reg8_write(client, 0x06, priv->fsync_period & 0xff); -+ reg8_write(client, 0x07, (priv->fsync_period >> 8) & 0xff); -+ reg8_write(client, 0x08, priv->fsync_period >> 16); -+ -+ reg8_write(client, 0x63, 0); /* disable overlap window */ -+ reg8_write(client, 0x64, 0); -+ reg8_write(client, 0x0c, 0x91 | (priv->vsync ? BIT(3) : 0) | (priv->hsync ? BIT(2) : 0)); /* enable HS/VS encoding, use D14/15 for HS/VS, invert HS/VS */ -+ reg8_write(client, 0x19, 0x0c); /* Drive HSTRAIL state for 120ns after the last payload bit */ -+} -+ -+static void max9286_gmsl_link_setup(struct i2c_client *client, int idx) ++static int imx390_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ -+ /* GMSL setup */ -+ client->addr = 0x40; /* MAX9271-CAMx I2C */ -+ reg8_write(client, 0x0d, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ -+ reg8_write(client, 0x07, 0x84 | (priv->pclk_rising_edge ? 0 : 0x10)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x02, 0xff); /* spread spectrum +-4%, pclk range automatic, Gbps automatic */ -+ usleep_range(2000, 2500); /* wait 2ms */ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); + -+ if (priv->ser_id == MAX96705_ID) { -+ /* setup crossbar in DBL mode: reverse DVP bus */ -+ reg8_write(client, 0x20, 0x07); -+ reg8_write(client, 0x21, 0x06); -+ reg8_write(client, 0x22, 0x05); -+ reg8_write(client, 0x23, 0x04); -+ reg8_write(client, 0x24, 0x03); -+ reg8_write(client, 0x25, 0x02); -+ reg8_write(client, 0x26, 0x01); -+ reg8_write(client, 0x27, 0x00); ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; + -+ reg8_write(client, 0x30, 0x17); -+ reg8_write(client, 0x31, 0x16); -+ reg8_write(client, 0x32, 0x15); -+ reg8_write(client, 0x33, 0x14); -+ reg8_write(client, 0x34, 0x13); -+ reg8_write(client, 0x35, 0x12); -+ reg8_write(client, 0x36, 0x11); -+ reg8_write(client, 0x37, 0x10); ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = IMX390_MAX_WIDTH; ++ sel->r.height = IMX390_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = IMX390_MAX_WIDTH; ++ sel->r.height = IMX390_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->rect; ++ return 0; ++ default: ++ return -EINVAL; + } -+ -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ reg8_write(client, 0x34, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ -+ /* I2C translator setup */ -+ client->addr = 0x40; /* MAX9271-CAMx I2C */ -+// reg8_write(client, 0x09, maxim_map[2][idx] << 1); /* SENSOR I2C translated - must be set by sensor driver */ -+// reg8_write(client, 0x0A, 0x30 << 1); /* SENSOR I2C native - must be set by sensor driver */ -+ reg8_write(client, 0x0B, BROADCAST << 1); /* broadcast I2C */ -+ reg8_write(client, 0x0C, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAMx I2C new */ -+ /* I2C addresse change */ -+ reg8_write(client, 0x01, priv->des_addr << 1); /* MAX9286 I2C */ -+ reg8_write(client, 0x00, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAM0 I2C new */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ /* put MAX9271 in configuration link state */ -+ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C new */ -+ reg8_write(client, 0x04, 0x43); /* enable reverse_control/conf_link */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+#ifdef MAXIM_DUMP -+ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ -+ maxim_max927x_dump_regs(client); -+ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C new */ -+ maxim_max927x_dump_regs(client); -+#endif +} + -+static int max9286_initialize(struct i2c_client *client) ++static int imx390_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ int idx, ret; -+ -+ dev_info(&client->dev, "LINKs=%d, LANES=%d, FSYNC mode=%s, FSYNC period=%d, PCLK edge=%s\n", -+ priv->links, priv->lanes, priv->fsync_mode, priv->fsync_period, -+ priv->pclk_rising_edge ? "rising" : "falling"); -+ -+ if (priv->des_quirk_addr) -+ max9286_preinit(client, priv->des_quirk_addr); -+ -+ max9286_preinit(client, priv->des_addr); -+ max9286_initial_setup(client); -+ -+ for (idx = 0; idx < priv->links; idx++) { -+ if (!IS_ERR(priv->poc_gpio[idx])) { -+ gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */ -+ mdelay(priv->poc_delay); -+ } -+ -+ ret = max9286_reverse_channel_setup(client, idx); -+ if (ret) -+ continue; -+ max9286_gmsl_link_setup(client, idx); -+ } -+ -+ max9286_postinit(client, priv->des_addr); -+ -+ client->addr = priv->des_addr; ++ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ cfg->type = V4L2_MBUS_CSI2; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG -+static int max9286_g_register(struct v4l2_subdev *sd, -+ struct v4l2_dbg_register *reg) ++static int imx390_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) +{ -+ struct max9286_priv *priv = v4l2_get_subdevdata(sd); -+ struct i2c_client *client = priv->client; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u8 val = 0; + -+ ret = reg8_read(client, (u8)reg->reg, &val); ++ ret = reg16_read(client, (u16)reg->reg, &val); + if (ret < 0) + return ret; + @@ -2512,187 +2358,138 @@ index 0000000..c850196 + return 0; +} + -+static int max9286_s_register(struct v4l2_subdev *sd, -+ const struct v4l2_dbg_register *reg) ++static int imx390_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) +{ -+ struct max9286_priv *priv = v4l2_get_subdevdata(sd); -+ struct i2c_client *client = priv->client; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); + -+ return reg8_write(client, (u8)reg->reg, (u8)reg->val); ++ return reg16_write(client, (u16)reg->reg, (u8)reg->val); +} +#endif + -+static int max9286_s_power(struct v4l2_subdev *sd, int on) ++static struct v4l2_subdev_core_ops imx390_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = imx390_g_register, ++ .s_register = imx390_s_register, ++#endif ++}; ++ ++static int imx390_s_ctrl(struct v4l2_ctrl *ctrl) +{ -+ struct max9286_priv *priv = v4l2_get_subdevdata(sd); -+ struct i2c_client *client = priv->client; ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); ++ int ret = -EINVAL; + -+ if (on) { -+ if (atomic_inc_return(&priv->use_count) == 1) -+ reg8_write(client, 0x69, priv->links_mask ^ 0x0f); /* unmask CSI forwarding from detected links */ -+ } else { -+ if (atomic_dec_return(&priv->use_count) == 0) -+ reg8_write(client, 0x69, 0x0f); /* mask CSI forwarding from all links */ ++ if (!priv->init_complete) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ case V4L2_CID_CONTRAST: ++ case V4L2_CID_SATURATION: ++ case V4L2_CID_HUE: ++ case V4L2_CID_GAMMA: ++ case V4L2_CID_SHARPNESS: ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_GAIN: ++ case V4L2_CID_EXPOSURE: ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ break; + } + -+ return 0; ++ return ret; +} + -+static int max9286_registered_async(struct v4l2_subdev *sd) -+{ -+ struct max9286_priv *priv = v4l2_get_subdevdata(sd); -+ struct i2c_client *client = priv->client; -+ int idx, tmp_addr; -+ -+ /* switch to GMSL serial_link for streaming video */ -+ tmp_addr = client->addr; -+ idx = sd->grp_id; -+ -+ client->addr = priv->des_addr; /* MAX9286 I2C */ -+ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */ ++static const struct v4l2_ctrl_ops imx390_ctrl_ops = { ++ .s_ctrl = imx390_s_ctrl, ++}; + -+ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx */ -+ reg8_write(client, 0x04, conf_link ? 0x43 : 0x83); /* enable serial_link */ -+ usleep_range(2000, 2500); /* wait 2ms after changing reverse_control */ ++static struct v4l2_subdev_video_ops imx390_video_ops = { ++ .s_stream = imx390_s_stream, ++ .g_mbus_config = imx390_g_mbus_config, ++}; + -+ client->addr = priv->des_addr; /* MAX9286 I2C */ -+ reg8_write(client, 0x0a, (priv->links_mask << 4) | priv->links_mask); /* enable reverse/forward control for all CAMs */ ++static const struct v4l2_subdev_pad_ops imx390_subdev_pad_ops = { ++ .get_edid = imx390_get_edid, ++ .enum_mbus_code = imx390_enum_mbus_code, ++ .get_selection = imx390_get_selection, ++ .set_selection = imx390_set_selection, ++ .get_fmt = imx390_get_fmt, ++ .set_fmt = imx390_set_fmt, ++}; + -+ client->addr = tmp_addr; ++static struct v4l2_subdev_ops imx390_subdev_ops = { ++ .core = &imx390_core_ops, ++ .video = &imx390_video_ops, ++ .pad = &imx390_subdev_pad_ops, ++}; + -+ return 0; ++static void imx390_otp_id_read(struct i2c_client *client) ++{ +} + -+static struct v4l2_subdev_core_ops max9286_subdev_core_ops = { -+#ifdef CONFIG_VIDEO_ADV_DEBUG -+ .g_register = max9286_g_register, -+ .s_register = max9286_s_register, -+#endif -+ .s_power = max9286_s_power, -+ .registered_async = max9286_registered_async, -+}; -+ -+static struct v4l2_subdev_ops max9286_subdev_ops = { -+ .core = &max9286_subdev_core_ops, -+}; -+ -+static int max9286_parse_dt(struct i2c_client *client) ++static ssize_t imx390_otp_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ struct device_node *np = client->dev.of_node; -+ struct device_node *endpoint = NULL; -+ struct property *prop; -+ int err, pwen, i; -+ int sensor_delay, gpio0 = 1, gpio1 = 1; -+ u8 val = 0; -+ char poc_name[10]; -+ -+ if (of_property_read_u32(np, "maxim,links", &priv->links)) -+ priv->links = 4; -+ -+ if (of_property_read_u32(np, "maxim,lanes", &priv->lanes)) -+ priv->lanes = 4; -+ -+ pwen = of_get_gpio(np, 0); -+ if (pwen > 0) { -+ err = gpio_request_one(pwen, GPIOF_OUT_INIT_HIGH, dev_name(&client->dev)); -+ if (err) -+ dev_err(&client->dev, "cannot request PWEN gpio %d: %d\n", pwen, err); -+ } ++ struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx390_priv *priv = to_imx390(client); + -+ mdelay(250); ++ return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++} + -+ for (i = 0; i < 4; i++) { -+ sprintf(poc_name, "POC%d", i); -+ priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, poc_name, 0); -+ } ++static DEVICE_ATTR(otp_id_imx390, S_IRUGO, imx390_otp_id_show, NULL); + -+ reg8_read(client, 0x1e, &val); /* read max9286 ID */ -+ if (val != MAX9286_ID) { -+ prop = of_find_property(np, "reg", NULL); -+ if (prop) -+ of_remove_property(np, prop); -+ return -ENODEV; -+ } ++static int imx390_initialize(struct i2c_client *client) ++{ ++ struct imx390_priv *priv = to_imx390(client); ++ u8 val = 0; ++ u16 pid; ++ int ret = 0; ++ int tmp_addr; + -+ if (!of_property_read_u32(np, "maxim,gpio0", &gpio0) || -+ !of_property_read_u32(np, "maxim,gpio1", &gpio1)) -+ reg8_write(client, 0x0f, 0x08 | (gpio1 << 1) | gpio0); ++ /* check and show model ID */ ++ reg16_read(client, IMX390_PID, &val); ++ pid = val; ++ reg16_read(client, IMX390_VER, &val); ++ pid = (pid << 8) | val; + -+ if (of_property_read_u32(np, "maxim,resetb-gpio", &priv->gpio_resetb)) { -+ priv->gpio_resetb = -1; -+ } else { -+ if (of_property_read_bool(np, "maxim,resetb-active-high")) -+ priv->active_low_resetb = 0; -+ else -+ priv->active_low_resetb = 1; ++ if (pid != IMX390_VERSION_REG) { ++ dev_dbg(&client->dev, "Product ID error %x\n", pid); ++ ret = -ENODEV; ++ goto err; + } + -+ if (!of_property_read_u32(np, "maxim,sensor_delay", &sensor_delay)) -+ mdelay(sensor_delay); -+ if (of_property_read_string(np, "maxim,fsync-mode", &priv->fsync_mode)) -+ priv->fsync_mode = fsync_mode_default; -+ if (of_property_read_u32(np, "maxim,fsync-period", &priv->fsync_period)) -+ priv->fsync_period = 3200000; /* 96MHz/30fps */ -+ priv->pclk_rising_edge = true; -+ if (of_property_read_bool(np, "maxim,pclk-falling-edge")) -+ priv->pclk_rising_edge = false; -+ if (of_property_read_u32(np, "maxim,timeout", &priv->timeout)) -+ priv->timeout = 100; -+ if (of_property_read_u32(np, "maxim,i2c-quirk", &priv->des_quirk_addr)) -+ priv->des_quirk_addr = 0; -+ if (of_property_read_u32(np, "maxim,him", &priv->him)) -+ priv->him = 0; -+ if (of_property_read_u32(np, "maxim,hsync", &priv->hsync)) -+ priv->hsync = 0; -+ if (of_property_read_u32(np, "maxim,vsync", &priv->vsync)) -+ priv->vsync = 1; -+ if (of_property_read_u32(np, "maxim,poc-delay", &priv->poc_delay)) -+ priv->poc_delay = 50; -+ -+ /* module params override dts */ -+ if (him) -+ priv->him = him; -+ if (fsync_period) { -+ priv->fsync_period = fsync_period; -+ priv->fsync_mode = fsync_mode_default; ++ /* setup XCLK */ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ /* CLK_OUT=22.5792*160*M/N/CLKDIV -> CLK_OUT=25MHz: CLKDIV=4, M=7, N=253: 22.5792*160/4*7/253=24.989MHz=CLK_OUT */ ++ client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++ reg8_write(client, 0x06, 0x47); /* Set CLKDIV and M */ ++ reg8_write(client, 0x07, 0xfd); /* Set N */ + } -+ if (hsync) -+ priv->hsync = hsync; -+ if (!vsync) -+ priv->vsync = vsync; -+ if (gpio_resetb) -+ priv->gpio_resetb = gpio_resetb; -+ if (active_low_resetb) -+ priv->active_low_resetb = active_low_resetb; -+ if (poc_delay) -+ priv->poc_delay = poc_delay; -+ -+ for (i = 0; i < priv->links; i++) { -+ endpoint = of_graph_get_next_endpoint(np, endpoint); -+ if (!endpoint) -+ break; -+ -+ of_node_put(endpoint); -+ -+ if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr_map[i])) { -+ dev_err(&client->dev, "max9271-addr not set\n"); -+ return -EINVAL; -+ } ++ client->addr = tmp_addr; + -+ priv->sd_of_node[i] = endpoint; -+ } ++ /* Program wizard registers */ ++ imx390_set_regs(client, imx390_regs_wizard, ARRAY_SIZE(imx390_regs_wizard)); ++ /* Read OTP IDs */ ++ imx390_otp_id_read(client); + -+ return 0; ++ dev_info(&client->dev, "imx390 PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ pid, IMX390_MAX_WIDTH, IMX390_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++err: ++ return ret; +} + -+static void max9286_setup_remote_endpoint(struct i2c_client *client) ++static int imx390_parse_dt(struct device_node *np, struct imx390_priv *priv) +{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ struct device_node *np = client->dev.of_node; -+ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); + int i; -+ struct property *csi_rate_prop, *dvp_order_prop; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ int tmp_addr = 0; + + for (i = 0; ; i++) { + endpoint = of_graph_get_next_endpoint(np, endpoint); @@ -2705,116 +2502,177 @@ index 0000000..c850196 + if (!rendpoint) + continue; + -+ csi_rate_prop = of_find_property(endpoint, "csi-rate", NULL); -+ if (csi_rate_prop) { -+ /* CSI2_RATE = PCLK*sizeof(YUV8)*links/lanes */ -+ priv->csi_rate = cpu_to_be32(100 * 8 * hweight8(priv->links_mask) / priv->lanes); -+ csi_rate_prop->value = &priv->csi_rate; -+ of_update_property(rendpoint, csi_rate_prop); -+ } ++ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && ++ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ } + -+ dvp_order_prop = of_find_property(endpoint, "dvp-order", NULL); -+ if (dvp_order_prop) -+ of_update_property(rendpoint, dvp_order_prop); ++ if (!priv->ti9x4_addr) { ++ dev_err(&client->dev, "deserializer does not present\n"); ++ return -EINVAL; ++ } ++ ++ /* setup I2C translator address */ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ client->addr = priv->ti9x4_addr; /* Deserializer I2C address */ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x5d, IMX390_I2C_ADDR << 1); /* Sensor native I2C address */ ++// reg8_write(client, 0x6e, 0xa9); /* GPIO0 - reset, GPIO1 - fsin */ + } ++ client->addr = tmp_addr; ++ ++ mdelay(10); ++ ++ return 0; +} + -+static int max9286_probe(struct i2c_client *client, -+ const struct i2c_device_id *did) ++static int imx390_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) +{ -+ struct max9286_priv *priv; -+ int err, i; ++ struct imx390_priv *priv; ++ int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + -+ i2c_set_clientdata(client, priv); -+ priv->des_addr = client->addr; -+ priv->client = client; -+ atomic_set(&priv->use_count, 0); -+ priv->csi2_outord = 0xff; ++ v4l2_i2c_subdev_init(&priv->sd, client, &imx390_subdev_ops); ++ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + -+ err = max9286_parse_dt(client); -+ if (err) -+ goto out; ++ priv->exposure = 0x100; ++ priv->gain = 0x100; ++ priv->autogain = 1; ++ v4l2_ctrl_handler_init(&priv->hdl, 4); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 16, 1, 7); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 16, 1, 7); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 7, 1, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_HUE, 0, 23, 1, 12); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_GAMMA, -128, 128, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_SHARPNESS, 0, 10, 1, 3); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&priv->hdl, &imx390_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ priv->sd.ctrl_handler = &priv->hdl; + -+ err = max9286_initialize(client); -+ if (err < 0) -+ goto out; ++ ret = priv->hdl.error; ++ if (ret) ++ goto cleanup; + -+ max9286_setup_remote_endpoint(client); ++ v4l2_ctrl_handler_setup(&priv->hdl); + -+ for (i = 0; i < 4; i++) { -+ v4l2_subdev_init(&priv->sd[i], &max9286_subdev_ops); -+ priv->sd[i].owner = client->dev.driver->owner; -+ priv->sd[i].dev = &client->dev; -+ priv->sd[i].grp_id = i; -+ v4l2_set_subdevdata(&priv->sd[i], priv); -+ priv->sd[i].of_node = priv->sd_of_node[i]; ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad); ++ if (ret < 0) ++ goto cleanup; + -+ snprintf(priv->sd[i].name, V4L2_SUBDEV_NAME_SIZE, "%s.%d %d-%04x", -+ client->dev.driver->name, i, i2c_adapter_id(client->adapter), -+ client->addr); ++ ret = imx390_parse_dt(client->dev.of_node, priv); ++ if (ret) ++ goto cleanup; + -+ err = v4l2_async_register_subdev(&priv->sd[i]); -+ if (err < 0) -+ goto out; -+ } -+out: -+ return err; -+} ++ ret = imx390_initialize(client); ++ if (ret < 0) ++ goto cleanup; + -+static int max9286_remove(struct i2c_client *client) -+{ -+ struct max9286_priv *priv = i2c_get_clientdata(client); -+ int i; ++ priv->rect.left = 0; ++ priv->rect.top = 0; ++ priv->rect.width = IMX390_MAX_WIDTH; ++ priv->rect.height = IMX390_MAX_HEIGHT; + -+ for (i = 0; i < 4; i++) { -+ v4l2_async_unregister_subdev(&priv->sd[i]); -+ v4l2_device_unregister_subdev(&priv->sd[i]); -+ } ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) ++ goto cleanup; + -+ return 0; ++ if (device_create_file(&client->dev, &dev_attr_otp_id_imx390) != 0) { ++ dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); ++ goto cleanup; ++ } ++ ++ priv->init_complete = 1; ++ ++ return 0; ++ ++cleanup: ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++#ifdef CONFIG_SOC_CAMERA_IMX390 ++ v4l_err(client, "failed to probe @ 0x%02x (%s)\n", ++ client->addr, client->adapter->name); ++#endif ++ return ret; +} + -+static const struct of_device_id max9286_dt_ids[] = { -+ { .compatible = "maxim,max9286" }, -+ {}, ++static int imx390_remove(struct i2c_client *client) ++{ ++ struct imx390_priv *priv = i2c_get_clientdata(client); ++ ++ device_remove_file(&client->dev, &dev_attr_otp_id_imx390); ++ v4l2_async_unregister_subdev(&priv->sd); ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SOC_CAMERA_IMX390 ++static const struct i2c_device_id imx390_id[] = { ++ { "imx390", 0 }, ++ { } +}; -+MODULE_DEVICE_TABLE(of, max9286_dt_ids); ++MODULE_DEVICE_TABLE(i2c, imx390_id); + -+static const struct i2c_device_id max9286_id[] = { -+ { "max9286", 0 }, ++static const struct of_device_id imx390_of_ids[] = { ++ { .compatible = "sony,imx390", }, + { } +}; -+MODULE_DEVICE_TABLE(i2c, max9286_id); ++MODULE_DEVICE_TABLE(of, imx390_of_ids); + -+static struct i2c_driver max9286_i2c_driver = { ++static struct i2c_driver imx390_i2c_driver = { + .driver = { -+ .name = "max9286", -+ .of_match_table = of_match_ptr(max9286_dt_ids), ++ .name = "imx390", ++ .of_match_table = imx390_of_ids, + }, -+ .probe = max9286_probe, -+ .remove = max9286_remove, -+ .id_table = max9286_id, ++ .probe = imx390_probe, ++ .remove = imx390_remove, ++ .id_table = imx390_id, +}; + -+module_i2c_driver(max9286_i2c_driver); ++module_i2c_driver(imx390_i2c_driver); + -+MODULE_DESCRIPTION("GMSL driver for MAX9286"); ++MODULE_DESCRIPTION("SoC Camera driver for IMX390"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); -diff --git a/drivers/media/i2c/soc_camera/max9286.h b/drivers/media/i2c/soc_camera/max9286.h ++#endif +diff --git a/drivers/media/i2c/soc_camera/imx390.h b/drivers/media/i2c/soc_camera/imx390.h new file mode 100644 -index 0000000..6c2a9e0 +index 0000000..4217cd9 --- /dev/null -+++ b/drivers/media/i2c/soc_camera/max9286.h -@@ -0,0 +1,244 @@ ++++ b/drivers/media/i2c/soc_camera/imx390.h +@@ -0,0 +1,3817 @@ +/* -+ * MAXIM max9286-max9271 GMSL driver include file ++ * OmniVision IMX390 sensor camera wizard 1920x1080@30/BGGR/MIPI + * -+ * Copyright (C) 2015-2017 Cogent Embedded, Inc. ++ * Copyright (C) 2018 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -2822,1012 +2680,4768 @@ index 0000000..6c2a9e0 + * option) any later version. + */ + -+#ifndef _MAX9286_MAX9271_H -+#define _MAX9286_MAX9271_H ++//#define IMX390_DISPLAY_PATTERN_COLOR_BAR + -+//#define DEBUG -+#ifdef DEBUG -+//#define WRITE_VERIFY -+#define MAXIM_DUMP -+#undef dev_dbg -+#define dev_dbg dev_info ++#define IMX390_MAX_WIDTH 1920 ++#define IMX390_MAX_HEIGHT 1080 ++ ++#define IMX390_DELAY 0xffff ++#define IMX390_DT 0x2c /* MIPI Data Type RAW12 */ ++ ++struct imx390_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++/* wizard: MIPI 1920x1080 RAW12 Linear 30fps 700Mbps */ ++static const struct imx390_reg imx390_regs_wizard[] = { ++{0x000C, 0xF2}, ++{0x000D, 0x02}, ++{0x000E, 0x00}, ++{0x0010, 0xF2}, ++{0x0011, 0x02}, ++{0x0012, 0x00}, ++{0x0018, 0x15}, ++{0x0019, 0x00}, ++{0x001A, 0x0C}, ++{0x001B, 0x00}, ++{0x0038, 0x00}, ++{0x003C, 0x00}, ++{0x003D, 0x00}, ++{0x003E, 0x00}, ++{0x0040, 0x00}, ++{0x0041, 0x00}, ++{0x0042, 0x00}, ++{0x0044, 0x00}, ++{0x0045, 0x00}, ++{0x0046, 0x00}, ++{0x0048, 0x00}, ++{0x0049, 0x00}, ++{0x004A, 0x00}, ++{0x004C, 0x00}, ++{0x004D, 0x00}, ++{0x004E, 0x00}, ++{0x0050, 0x00}, ++{0x0051, 0x00}, ++{0x0052, 0x00}, ++{0x0054, 0x00}, ++{0x0055, 0x00}, ++{0x0056, 0x00}, ++{0x0058, 0x00}, ++{0x0059, 0x00}, ++{0x005A, 0x00}, ++{0x005C, 0x00}, ++{0x005D, 0x00}, ++{0x005E, 0x00}, ++{0x0060, 0x00}, ++{0x0061, 0x00}, ++{0x0062, 0x00}, ++{0x0064, 0x00}, ++{0x0065, 0x00}, ++{0x0066, 0x00}, ++{0x0068, 0x00}, ++{0x0069, 0x00}, ++{0x006A, 0x00}, ++{0x0078, 0x00}, ++{0x007C, 0x00}, ++{0x007D, 0x00}, ++{0x0080, 0x00}, ++{0x0081, 0x00}, ++{0x00F4, 0x1C}, ++{0x00F5, 0xF8}, ++{0x00F6, 0x01}, ++{0x00F8, 0x03}, ++{0x00F9, 0x00}, ++{0x00FA, 0x00}, ++{0x00FB, 0x00}, ++{0x0114, 0x00}, ++{0x0115, 0x01}, ++{0x0118, 0x20}, ++{0x0119, 0x03}, ++{0x011A, 0x00}, ++{0x011B, 0x41}, ++{0x011C, 0x80}, ++{0x011D, 0x00}, ++{0x0120, 0x20}, ++{0x0121, 0x00}, ++{0x0122, 0x00}, ++{0x0123, 0x44}, ++{0x0124, 0x00}, ++{0x0125, 0x01}, ++{0x0128, 0xAC}, ++{0x0129, 0x0D}, ++{0x012A, 0x00}, ++{0x012B, 0xA4}, ++{0x012C, 0x00}, ++{0x012D, 0x01}, ++{0x0130, 0xC4}, ++{0x0131, 0x09}, ++{0x0132, 0x00}, ++{0x0133, 0xDA}, ++//{0x013A, ,3}, ++{0x013B, 0x01}, ++//{0x013C, ,3}, ++//{0x013D, ,3}, ++//{0x013E, ,3}, ++//{0x0140, ,3}, ++//{0x0141, ,3}, ++//{0x0142, ,3}, ++//{0x0144, ,3}, ++//{0x0145, ,3}, ++//{0x0146, ,3}, ++//{0x0148, ,3}, ++//{0x0149, ,3}, ++//{0x014A, ,3}, ++//{0x014C, ,3}, ++//{0x014D, ,3}, ++//{0x014E, ,3}, ++//{0x0150, ,3}, ++//{0x0151, ,3}, ++//{0x0152, ,3}, ++//{0x0154, ,3}, ++//{0x0155, ,3}, ++//{0x0156, ,3}, ++//{0x0158, ,3}, ++//{0x0159, ,3}, ++//{0x015A, ,3}, ++//{0x015C, ,3}, ++//{0x015D, ,3}, ++//{0x015E, ,3}, ++//{0x0160, ,3}, ++//{0x0161, ,3}, ++//{0x0162, ,3}, ++//{0x0164, ,3}, ++//{0x0165, ,3}, ++//{0x0166, ,3}, ++//{0x0168, ,3}, ++//{0x0169, ,3}, ++//{0x016A, ,3}, ++//{0x016C, ,3}, ++//{0x016D, ,3}, ++//{0x016E, ,3}, ++//{0x0170, ,3}, ++//{0x0171, ,3}, ++//{0x0172, ,3}, ++//{0x0174, ,3}, ++//{0x0175, ,3}, ++//{0x0176, ,3}, ++//{0x0178, ,3}, ++//{0x0179, ,3}, ++//{0x017A, ,3}, ++//{0x017C, ,3}, ++//{0x017D, ,3}, ++//{0x017E, ,3}, ++//{0x0180, ,3}, ++//{0x0181, ,3}, ++//{0x0182, ,3}, ++//{0x0184, ,3}, ++//{0x0185, ,3}, ++//{0x0186, ,3}, ++//{0x0188, ,3}, ++//{0x0189, ,3}, ++//{0x018A, ,3}, ++//{0x018C, ,3}, ++//{0x018D, ,3}, ++//{0x018E, ,3}, ++//{0x0190, ,3}, ++//{0x0191, ,3}, ++//{0x0192, ,3}, ++//{0x0194, ,3}, ++//{0x0195, ,3}, ++//{0x0196, ,3}, ++//{0x0198, ,3}, ++//{0x0199, ,3}, ++//{0x019A, ,3}, ++//{0x019B, ,3}, ++//{0x019C, ,3}, ++//{0x019D, ,3}, ++//{0x019E, ,3}, ++//{0x019F, ,3}, ++//{0x01A0, ,3}, ++//{0x01A1, ,3}, ++//{0x01A2, ,3}, ++//{0x01A3, ,3}, ++//{0x01A4, ,3}, ++//{0x01A5, ,3}, ++//{0x01A6, ,3}, ++//{0x01A7, ,3}, ++//{0x01A8, ,3}, ++//{0x01A9, ,3}, ++//{0x01AA, ,3}, ++//{0x01AB, ,3}, ++//{0x01AC, ,3}, ++//{0x01AD, ,3}, ++//{0x01AE, ,3}, ++//{0x01AF, ,3}, ++//{0x01B0, ,3}, ++//{0x01B1, ,3}, ++//{0x01B2, ,3}, ++//{0x01B3, ,3}, ++//{0x01B4, ,3}, ++//{0x01B5, ,3}, ++//{0x01B6, ,3}, ++//{0x01B7, ,3}, ++//{0x01B8, ,3}, ++//{0x01B9, ,3}, ++//{0x01BA, ,3}, ++//{0x01BB, ,3}, ++//{0x01BC, ,3}, ++//{0x01BD, ,3}, ++//{0x01BE, ,3}, ++//{0x01BF, ,3}, ++//{0x01C0, ,3}, ++//{0x01C1, ,3}, ++//{0x01C2, ,3}, ++//{0x01C3, ,3}, ++{0x01C4, 0x00}, ++{0x01C5, 0x00}, ++{0x01CC, 0x01}, ++{0x01D0, 0x09}, ++{0x01D4, 0x01}, ++{0x0232, 0x7E}, ++{0x0233, 0x00}, ++{0x0390, 0x00}, ++{0x0391, 0x00}, ++{0x0392, 0x00}, ++#ifdef IMX390_DISPLAY_PATTERN_COLOR_BAR ++{0x01DB, 0x32}, ++{0x03C0, 0x02}, ++#else ++{0x03C0, 0x00}, ++#endif ++{0x2000, 0x55}, ++{0x2001, 0x55}, ++{0x2002, 0x55}, ++{0x2003, 0x05}, ++{0x2004, 0x02}, ++{0x2008, 0x65}, ++{0x2009, 0x04}, ++{0x200A, 0x00}, ++{0x200C, 0x30}, ++{0x200D, 0x11}, ++{0x2010, 0x04}, ++{0x2014, 0x01}, ++{0x2018, 0x02}, ++{0x2019, 0x04}, ++{0x201A, 0x00}, ++{0x201C, 0x21}, ++{0x201D, 0x11}, ++{0x201E, 0x00}, ++{0x201F, 0x00}, ++{0x2020, 0xBC}, ++{0x2021, 0x00}, ++{0x2022, 0x7F}, ++{0x2023, 0x00}, ++{0x2024, 0xBA}, ++{0x2025, 0x00}, ++{0x2026, 0x81}, ++{0x2027, 0x00}, ++{0x2028, 0x7D}, ++{0x2029, 0x90}, ++{0x202A, 0x05}, ++{0x202C, 0xFC}, ++{0x202D, 0x02}, ++{0x202E, 0x25}, ++{0x202F, 0x03}, ++{0x2030, 0x05}, ++{0x2031, 0x02}, ++{0x2032, 0xCA}, ++{0x2033, 0x02}, ++{0x2034, 0xFC}, ++{0x2035, 0x02}, ++{0x2036, 0x25}, ++{0x2037, 0x03}, ++{0x2038, 0x25}, ++{0x2039, 0x97}, ++{0x203A, 0xEC}, ++{0x203B, 0x01}, ++{0x203C, 0xF5}, ++{0x203D, 0x8E}, ++{0x203E, 0x0C}, ++{0x203F, 0x2D}, ++{0x2040, 0x69}, ++{0x2041, 0x01}, ++{0x2042, 0x8E}, ++{0x2043, 0x01}, ++{0x2044, 0x0C}, ++{0x2045, 0x02}, ++{0x2046, 0x31}, ++{0x2047, 0x02}, ++{0x2048, 0x6A}, ++{0x2049, 0x01}, ++{0x204A, 0x8E}, ++{0x204B, 0x01}, ++{0x204C, 0x0D}, ++{0x204D, 0x02}, ++{0x204E, 0x31}, ++{0x204F, 0x02}, ++{0x2050, 0x7B}, ++{0x2051, 0x00}, ++{0x2052, 0x7D}, ++{0x2053, 0x00}, ++{0x2054, 0x95}, ++{0x2055, 0x00}, ++{0x2056, 0x97}, ++{0x2057, 0x00}, ++{0x2058, 0xAD}, ++{0x2059, 0x00}, ++{0x205A, 0xAF}, ++{0x205B, 0x00}, ++{0x205C, 0x92}, ++{0x205D, 0x00}, ++{0x205E, 0x94}, ++{0x205F, 0x00}, ++{0x2060, 0x8E}, ++{0x2061, 0x00}, ++{0x2062, 0x90}, ++{0x2063, 0x00}, ++{0x2064, 0xB1}, ++{0x2065, 0x00}, ++{0x2066, 0xB3}, ++{0x2067, 0x00}, ++{0x2068, 0x08}, ++{0x2069, 0x00}, ++{0x206A, 0x04}, ++{0x206B, 0x00}, ++{0x206C, 0x84}, ++{0x206D, 0x00}, ++{0x206E, 0x80}, ++{0x206F, 0x00}, ++{0x2070, 0x04}, ++{0x2071, 0x00}, ++{0x2072, 0x46}, ++{0x2073, 0x00}, ++{0x2074, 0xE9}, ++{0x2075, 0x01}, ++{0x2076, 0x74}, ++{0x2077, 0x02}, ++{0x2078, 0x80}, ++{0x2079, 0x00}, ++{0x207A, 0xC1}, ++{0x207B, 0x00}, ++{0x207C, 0xFF}, ++{0x207D, 0x03}, ++{0x207E, 0xFF}, ++{0x207F, 0x03}, ++{0x2080, 0x78}, ++{0x2081, 0x00}, ++{0x2082, 0x6A}, ++{0x2083, 0x01}, ++{0x2084, 0xE4}, ++{0x2085, 0x01}, ++{0x2086, 0x2B}, ++{0x2087, 0x03}, ++{0x2088, 0x00}, ++{0x2089, 0x00}, ++{0x208A, 0xFF}, ++{0x208B, 0x03}, ++{0x208C, 0xFF}, ++{0x208D, 0x03}, ++{0x208E, 0xFF}, ++{0x208F, 0x03}, ++{0x2090, 0x7D}, ++{0x2091, 0x00}, ++{0x2092, 0x62}, ++{0x2093, 0x01}, ++{0x2094, 0xE9}, ++{0x2095, 0x01}, ++{0x2096, 0x00}, ++{0x2097, 0x00}, ++{0x2098, 0x7C}, ++{0x2099, 0x00}, ++{0x209A, 0x21}, ++{0x209B, 0x03}, ++{0x209C, 0xE9}, ++{0x209D, 0x01}, ++{0x209E, 0x21}, ++{0x209F, 0x03}, ++{0x20A0, 0xFF}, ++{0x20A1, 0x03}, ++{0x20A2, 0xFF}, ++{0x20A3, 0x03}, ++{0x20A4, 0xFF}, ++{0x20A5, 0x03}, ++{0x20A6, 0xFF}, ++{0x20A7, 0x03}, ++{0x20A8, 0xFF}, ++{0x20A9, 0x03}, ++{0x20AA, 0xFF}, ++{0x20AB, 0x03}, ++{0x20AC, 0xFF}, ++{0x20AD, 0x03}, ++{0x20AE, 0xFF}, ++{0x20AF, 0x03}, ++{0x20B0, 0xFF}, ++{0x20B1, 0x03}, ++{0x20B2, 0xFF}, ++{0x20B3, 0x03}, ++{0x20B4, 0x87}, ++{0x20B5, 0xCC}, ++{0x20B6, 0x87}, ++{0x20B7, 0x08}, ++{0x20B8, 0xF4}, ++{0x20B9, 0xA5}, ++{0x20BA, 0x07}, ++{0x20BC, 0x1F}, ++{0x20BD, 0x01}, ++{0x20BE, 0xF6}, ++{0x20BF, 0x00}, ++{0x20C0, 0x90}, ++{0x20C1, 0x01}, ++{0x20C2, 0x67}, ++{0x20C3, 0x01}, ++{0x20C4, 0xFF}, ++{0x20C5, 0x03}, ++{0x20C6, 0xFF}, ++{0x20C7, 0x03}, ++{0x20C8, 0x33}, ++{0x20C9, 0x02}, ++{0x20CA, 0x0A}, ++{0x20CB, 0x02}, ++{0x20CC, 0x7F}, ++{0x20CD, 0x00}, ++{0x20CE, 0xD2}, ++{0x20CF, 0x00}, ++{0x20D0, 0x81}, ++{0x20D1, 0x00}, ++{0x20D2, 0x87}, ++{0x20D3, 0x00}, ++{0x20D4, 0x09}, ++{0x20D5, 0x00}, ++{0x20D8, 0x7F}, ++{0x20D9, 0x00}, ++{0x20DA, 0x62}, ++{0x20DB, 0x01}, ++{0x20DC, 0x7F}, ++{0x20DD, 0x00}, ++{0x20DE, 0x62}, ++{0x20DF, 0x01}, ++{0x20E0, 0x65}, ++{0x20E1, 0x00}, ++{0x20E2, 0x75}, ++{0x20E3, 0x00}, ++{0x20E4, 0xE0}, ++{0x20E5, 0x00}, ++{0x20E6, 0xF0}, ++{0x20E7, 0x00}, ++{0x20E8, 0x4C}, ++{0x20E9, 0x01}, ++{0x20EA, 0x5C}, ++{0x20EB, 0x01}, ++{0x20EC, 0xD1}, ++{0x20ED, 0x01}, ++{0x20EE, 0xE1}, ++{0x20EF, 0x01}, ++{0x20F0, 0x93}, ++{0x20F1, 0x02}, ++{0x20F2, 0xA3}, ++{0x20F3, 0x02}, ++{0x20F4, 0x0D}, ++{0x20F5, 0x03}, ++{0x20F6, 0x1D}, ++{0x20F7, 0x03}, ++{0x20F8, 0x57}, ++{0x20F9, 0x00}, ++{0x20FA, 0x7B}, ++{0x20FB, 0x00}, ++{0x20FC, 0xD2}, ++{0x20FD, 0x00}, ++{0x20FE, 0xF6}, ++{0x20FF, 0x00}, ++{0x2100, 0x3E}, ++{0x2101, 0x01}, ++{0x2102, 0x60}, ++{0x2103, 0x01}, ++{0x2104, 0xC3}, ++{0x2105, 0x01}, ++{0x2106, 0xE5}, ++{0x2107, 0x01}, ++{0x2108, 0x85}, ++{0x2109, 0x02}, ++{0x210A, 0xA9}, ++{0x210B, 0x02}, ++{0x210C, 0xFF}, ++{0x210D, 0x02}, ++{0x210E, 0x21}, ++{0x210F, 0x03}, ++{0x2110, 0xFF}, ++{0x2111, 0x03}, ++{0x2112, 0x00}, ++{0x2113, 0x00}, ++{0x2114, 0xFF}, ++{0x2115, 0x03}, ++{0x2116, 0xFF}, ++{0x2117, 0x03}, ++{0x2118, 0xFF}, ++{0x2119, 0x03}, ++{0x211A, 0xFF}, ++{0x211B, 0x03}, ++{0x211C, 0xFF}, ++{0x211D, 0x03}, ++{0x211E, 0xFF}, ++{0x211F, 0x03}, ++{0x2120, 0xFF}, ++{0x2121, 0x03}, ++{0x2122, 0xFF}, ++{0x2123, 0x03}, ++{0x2124, 0xFF}, ++{0x2125, 0x03}, ++{0x2126, 0xFF}, ++{0x2127, 0x03}, ++{0x2128, 0x7D}, ++{0x2129, 0x90}, ++{0x212A, 0xD5}, ++{0x212B, 0x07}, ++{0x212C, 0x64}, ++{0x212D, 0x01}, ++{0x2130, 0x5F}, ++{0x2131, 0x7D}, ++{0x2132, 0x05}, ++{0x2134, 0x78}, ++{0x2135, 0x00}, ++{0x2136, 0x76}, ++{0x2137, 0x00}, ++{0x2138, 0xF3}, ++{0x2139, 0x00}, ++{0x213A, 0xF1}, ++{0x213B, 0x00}, ++{0x213C, 0xA6}, ++{0x213D, 0x02}, ++{0x213E, 0xA4}, ++{0x213F, 0x02}, ++{0x2140, 0x7D}, ++{0x2141, 0x00}, ++{0x2142, 0x8D}, ++{0x2143, 0x00}, ++{0x2144, 0xA1}, ++{0x2145, 0x01}, ++{0x2146, 0xB1}, ++{0x2147, 0x01}, ++{0x2148, 0xAB}, ++{0x2149, 0x02}, ++{0x214A, 0xBB}, ++{0x214B, 0x02}, ++{0x214C, 0x17}, ++{0x214D, 0x5C}, ++{0x214E, 0x00}, ++{0x2150, 0x00}, ++{0x2151, 0x00}, ++{0x2152, 0xF8}, ++{0x2153, 0x00}, ++{0x2154, 0xBE}, ++{0x2155, 0x00}, ++{0x2156, 0x7D}, ++{0x2157, 0x00}, ++{0x2158, 0x25}, ++{0x2159, 0x00}, ++{0x215A, 0x7D}, ++{0x215B, 0x00}, ++{0x215C, 0x62}, ++{0x215D, 0x01}, ++{0x215E, 0xFF}, ++{0x215F, 0x03}, ++{0x2160, 0x26}, ++{0x2161, 0x00}, ++{0x2162, 0x7D}, ++{0x2163, 0x00}, ++{0x2164, 0x63}, ++{0x2165, 0x01}, ++{0x2166, 0xFF}, ++{0x2167, 0x03}, ++{0x2168, 0xCB}, ++{0x2169, 0x02}, ++{0x216A, 0xCF}, ++{0x216B, 0x02}, ++{0x216C, 0xFF}, ++{0x216D, 0x03}, ++{0x216E, 0xFF}, ++{0x216F, 0x03}, ++{0x2170, 0xFF}, ++{0x2171, 0x03}, ++{0x2172, 0xFF}, ++{0x2173, 0x03}, ++{0x2174, 0xFF}, ++{0x2175, 0x03}, ++{0x2176, 0xFF}, ++{0x2177, 0x03}, ++{0x2178, 0x7E}, ++{0x2179, 0x00}, ++{0x217A, 0xBD}, ++{0x217B, 0x00}, ++{0x217C, 0xEC}, ++{0x217D, 0x01}, ++{0x217E, 0x7B}, ++{0x217F, 0x02}, ++{0x2180, 0xD1}, ++{0x2181, 0x02}, ++{0x2182, 0x25}, ++{0x2183, 0x03}, ++{0x2184, 0x7F}, ++{0x2185, 0x00}, ++{0x2186, 0xBD}, ++{0x2187, 0x00}, ++{0x2188, 0xED}, ++{0x2189, 0x01}, ++{0x218A, 0x7B}, ++{0x218B, 0x02}, ++{0x218C, 0xD2}, ++{0x218D, 0x02}, ++{0x218E, 0x25}, ++{0x218F, 0x03}, ++{0x2190, 0xFF}, ++{0x2191, 0x03}, ++{0x2192, 0xFF}, ++{0x2193, 0x03}, ++{0x2194, 0xE9}, ++{0x2195, 0x01}, ++{0x2196, 0x21}, ++{0x2197, 0x03}, ++{0x2198, 0x17}, ++{0x2199, 0xFC}, ++{0x219A, 0x7F}, ++{0x219B, 0x01}, ++{0x219C, 0xFF}, ++{0x219D, 0x03}, ++{0x21A0, 0x1B}, ++{0x21A1, 0x1B}, ++{0x21A2, 0x1B}, ++{0x21A3, 0x1B}, ++{0x21A4, 0x2E}, ++{0x21A5, 0x80}, ++{0x21A6, 0x00}, ++{0x21A8, 0x04}, ++{0x21A9, 0x98}, ++{0x21AA, 0x60}, ++{0x21AB, 0x03}, ++{0x21AC, 0x7F}, ++{0x21AD, 0x80}, ++{0x21AE, 0x09}, ++{0x21B0, 0x1C}, ++{0x21B1, 0x00}, ++{0x21B2, 0xA0}, ++{0x21B3, 0x00}, ++{0x21B4, 0x0C}, ++{0x21B5, 0x00}, ++{0x21B6, 0x2D}, ++{0x21B7, 0x00}, ++{0x21B8, 0x20}, ++{0x21B9, 0x00}, ++{0x21BA, 0x02}, ++{0x21BB, 0x00}, ++{0x21BC, 0xCC}, ++{0x21BD, 0x00}, ++{0x21BE, 0x4A}, ++{0x21BF, 0x00}, ++{0x21C0, 0xD0}, ++{0x21C1, 0x00}, ++{0x21C2, 0x44}, ++{0x21C3, 0x00}, ++{0x21C4, 0x00}, ++{0x21C5, 0xE0}, ++{0x21C6, 0x00}, ++{0x21C8, 0x11}, ++{0x21C9, 0x00}, ++{0x21CA, 0x02}, ++{0x21CC, 0x08}, ++{0x21CD, 0xC0}, ++{0x21CE, 0x0C}, ++{0x21D0, 0x44}, ++{0x21D1, 0x00}, ++{0x21D2, 0x02}, ++{0x21D4, 0x02}, ++{0x21D5, 0x20}, ++{0x21D6, 0x2C}, ++{0x21D8, 0xFE}, ++{0x21D9, 0x9D}, ++{0x21DA, 0xDF}, ++{0x21DB, 0x03}, ++{0x21DC, 0x62}, ++{0x21DD, 0x01}, ++{0x21DE, 0x7F}, ++{0x21DF, 0x00}, ++{0x21E0, 0xB7}, ++{0x21E1, 0x01}, ++{0x21E2, 0xB5}, ++{0x21E3, 0x01}, ++{0x21E4, 0xC1}, ++{0x21E5, 0x02}, ++{0x21E6, 0xBF}, ++{0x21E7, 0x02}, ++{0x21E8, 0xB3}, ++{0x21E9, 0x0D}, ++{0x21EA, 0x00}, ++{0x21EB, 0x04}, ++#if 1 ++{0x21EC, 0x90}, ++{0x21ED, 0x07}, ++{0x21EE, 0x58}, ++{0x21EF, 0x04}, ++#else ++{0x21EC, 0x80}, ++{0x21ED, 0x07}, ++{0x21EE, 0x38}, ++{0x21EF, 0x04}, ++#endif ++{0x21F0, 0x54}, ++{0x21F1, 0x04}, ++{0x21F4, 0x02}, ++{0x21F5, 0x00}, ++{0x21F6, 0x00}, ++{0x21F8, 0x3C}, ++{0x21F9, 0x00}, ++{0x21FC, 0x28}, ++{0x21FD, 0x00}, ++{0x21FE, 0x3C}, ++{0x21FF, 0x00}, ++{0x2200, 0x00}, ++{0x2204, 0x4C}, ++{0x2205, 0x04}, ++{0x2206, 0x65}, ++{0x2207, 0x04}, ++{0x2208, 0x0A}, ++{0x2209, 0x00}, ++{0x220C, 0x47}, ++{0x220D, 0x00}, ++{0x220E, 0x1F}, ++{0x220F, 0x00}, ++{0x2210, 0x17}, ++{0x2211, 0x00}, ++{0x2212, 0x0F}, ++{0x2213, 0x00}, ++{0x2214, 0x17}, ++{0x2215, 0x00}, ++{0x2216, 0x47}, ++{0x2217, 0x00}, ++{0x2218, 0x0F}, ++{0x2219, 0x00}, ++{0x221A, 0x0F}, ++{0x221B, 0x00}, ++{0x221C, 0x03}, ++{0x2220, 0x20}, ++{0x2221, 0x20}, ++{0x2222, 0x22}, ++{0x2223, 0x02}, ++{0x2224, 0xA7}, ++{0x2225, 0xAA}, ++{0x2226, 0x80}, ++{0x2227, 0x08}, ++{0x2228, 0x01}, ++{0x22B2, 0x92}, ++{0x22B4, 0x20}, ++{0x22B5, 0x00}, ++{0x22B6, 0x20}, ++{0x22B7, 0x00}, ++{0x22B8, 0x20}, ++{0x22B9, 0x00}, ++{0x22BA, 0x20}, ++{0x22BB, 0x00}, ++{0x22BC, 0x20}, ++{0x22BD, 0x00}, ++{0x22BE, 0x20}, ++{0x22BF, 0x00}, ++{0x22C0, 0x20}, ++{0x22C1, 0x00}, ++{0x22C2, 0x20}, ++{0x22C3, 0x00}, ++{0x22C4, 0x20}, ++{0x22C5, 0x00}, ++{0x22C6, 0x20}, ++{0x22C7, 0x00}, ++{0x22C8, 0x20}, ++{0x22C9, 0x00}, ++{0x22CA, 0x20}, ++{0x22CB, 0x00}, ++{0x22CC, 0x20}, ++{0x22CD, 0x00}, ++{0x22CE, 0x20}, ++{0x22CF, 0x00}, ++{0x22DA, 0x00}, ++{0x2308, 0x01}, ++{0x2311, 0x09}, ++{0x2318, 0x40}, ++{0x2319, 0xCD}, ++{0x231A, 0x54}, ++{0x2324, 0x20}, ++{0x2325, 0x00}, ++{0x2328, 0x00}, ++{0x2354, 0x0C}, ++{0x23C0, 0x5D}, ++{0x244C, 0x00}, ++{0x244D, 0x02}, ++{0x244E, 0x54}, ++{0x244F, 0x02}, ++{0x24A0, 0x00}, ++{0x24DA, 0x6F}, ++{0x24DB, 0x00}, ++{0x24DC, 0x62}, ++{0x24DD, 0x01}, ++{0x24EA, 0x32}, ++{0x24EB, 0x00}, ++{0x24EC, 0xDC}, ++{0x24ED, 0x00}, ++{0x24FA, 0x32}, ++{0x24FB, 0x00}, ++{0x24FC, 0xDD}, ++{0x24FD, 0x00}, ++{0x254A, 0x15}, ++{0x254B, 0x01}, ++{0x255A, 0x15}, ++{0x255B, 0x01}, ++{0x2560, 0x01}, ++{0x2561, 0x00}, ++{0x2562, 0x2A}, ++{0x2563, 0x00}, ++{0x2564, 0xF8}, ++{0x2565, 0x00}, ++{0x2566, 0x15}, ++{0x2567, 0x01}, ++{0x2568, 0x0C}, ++{0x2569, 0x02}, ++{0x256A, 0x31}, ++{0x256B, 0x02}, ++{0x2578, 0x90}, ++{0x2579, 0x01}, ++{0x257A, 0x92}, ++{0x257B, 0x01}, ++{0x257C, 0xB8}, ++{0x257D, 0x02}, ++{0x257E, 0xBA}, ++{0x257F, 0x02}, ++{0x2584, 0x90}, ++{0x2585, 0x01}, ++{0x2586, 0x92}, ++{0x2587, 0x01}, ++{0x2588, 0xB8}, ++{0x2589, 0x02}, ++{0x258A, 0xBA}, ++{0x258B, 0x02}, ++{0x26B8, 0x10}, ++{0x26B9, 0x00}, ++{0x26BA, 0x33}, ++{0x26BB, 0x00}, ++{0x26BC, 0x89}, ++{0x26BD, 0x00}, ++{0x26BE, 0xB0}, ++{0x26BF, 0x00}, ++{0x26C4, 0x4E}, ++{0x26C5, 0x00}, ++{0x26C8, 0xC9}, ++{0x26C9, 0x00}, ++{0x26CC, 0x35}, ++{0x26CD, 0x01}, ++{0x26D0, 0xBA}, ++{0x26D1, 0x01}, ++{0x26D4, 0x7C}, ++{0x26D5, 0x02}, ++{0x26D8, 0xF6}, ++{0x26D9, 0x02}, ++{0x26DE, 0x51}, ++{0x26DF, 0x00}, ++{0x26E0, 0x7F}, ++{0x26E1, 0x00}, ++{0x26E2, 0xCC}, ++{0x26E3, 0x00}, ++{0x26E4, 0xF8}, ++{0x26E5, 0x00}, ++{0x26E6, 0x38}, ++{0x26E7, 0x01}, ++{0x26E8, 0x65}, ++{0x26E9, 0x01}, ++{0x26EA, 0xBD}, ++{0x26EB, 0x01}, ++{0x26EE, 0x7F}, ++{0x26EF, 0x02}, ++{0x26F0, 0xAB}, ++{0x26F1, 0x02}, ++{0x26F2, 0xF9}, ++{0x26F3, 0x02}, ++{0x2722, 0x59}, ++{0x2723, 0x02}, ++{0x2938, 0x55}, ++{0x2939, 0x00}, ++{0x293A, 0x17}, ++{0x293B, 0x00}, ++{0x293C, 0xD0}, ++{0x293D, 0x00}, ++{0x293E, 0x91}, ++{0x293F, 0x00}, ++{0x2940, 0x3C}, ++{0x2941, 0x01}, ++{0x2942, 0x0C}, ++{0x2943, 0x01}, ++{0x2944, 0xC1}, ++{0x2945, 0x01}, ++{0x2946, 0x76}, ++{0x2947, 0x01}, ++{0x2948, 0x83}, ++{0x2949, 0x02}, ++{0x294A, 0xFB}, ++{0x294B, 0x01}, ++{0x294C, 0xFD}, ++{0x294D, 0x02}, ++{0x294E, 0xBF}, ++{0x294F, 0x02}, ++{0x2A06, 0xFF}, ++{0x2A07, 0x03}, ++{0x2A20, 0x00}, ++{0x2A21, 0x00}, ++{0x2A22, 0x7D}, ++{0x2A23, 0x00}, ++{0x2B11, 0x19}, ++{0x2B13, 0x15}, ++{0x2B14, 0x14}, ++{0x2B15, 0x13}, ++{0x2B16, 0x12}, ++{0x2B17, 0x11}, ++{0x2B18, 0x10}, ++{0x2B19, 0x0F}, ++{0x2B1A, 0x0E}, ++{0x2B1B, 0x0D}, ++{0x2B1C, 0x0C}, ++{0x2B1D, 0x0B}, ++{0x2B1E, 0x0A}, ++{0x2B1F, 0x09}, ++{0x2B20, 0x08}, ++{0x2B21, 0x07}, ++{0x2B22, 0x06}, ++{0x2B23, 0x05}, ++{0x2B24, 0x04}, ++{0x2B25, 0x03}, ++{0x2B26, 0x03}, ++{0x2B38, 0x01}, ++{0x2B45, 0xE3}, ++{0x2B50, 0x01}, ++{0x2B51, 0x00}, ++//{0x2B62, ,3}, ++{0x2B6D, 0x47}, ++{0x2B70, 0x02}, ++{0x2B71, 0x02}, ++{0x2B72, 0x02}, ++{0x2B7F, 0x7F}, ++{0x2B80, 0x94}, ++{0x2B81, 0x06}, ++{0x2B87, 0x1B}, ++{0x2B88, 0x1B}, ++{0x2B89, 0x17}, ++{0x2B8A, 0x12}, ++{0x2B8B, 0x12}, ++{0x2B8D, 0x2B}, ++{0x2B8E, 0x2B}, ++{0x2B8F, 0x2B}, ++{0x2B90, 0x7F}, ++{0x2B91, 0x1F}, ++{0x2B94, 0x7F}, ++{0x2B95, 0x27}, ++{0x2B98, 0x7F}, ++{0x2B99, 0x57}, ++{0x2BA8, 0xBC}, ++{0x2BA9, 0x62}, ++{0x2BC1, 0x70}, ++{0x2BC5, 0x80}, ++{0x2BD5, 0x30}, ++{0x2BD6, 0xF0}, ++{0x2BD8, 0xDB}, ++{0x2BD9, 0xF6}, ++{0x2BDA, 0x63}, ++{0x2BDB, 0x0C}, ++{0x2BDC, 0x5C}, ++{0x2C98, 0xE1}, ++{0x2C99, 0x2E}, ++{0x2C9B, 0x86}, ++{0x2CA9, 0x80}, ++{0x2CAA, 0x01}, ++{0x2D39, 0x0E}, ++{0x2D54, 0x00}, ++{0x2D5B, 0x58}, ++{0x3000, 0x00}, ++{0x3001, 0x40}, ++{0x3002, 0x23}, ++{0x3003, 0xA1}, ++{0x3004, 0x00}, ++{0x3005, 0x20}, ++{0x3006, 0x94}, ++{0x3007, 0x00}, ++{0x3008, 0x06}, ++{0x3009, 0xB4}, ++{0x300A, 0x1F}, ++{0x300B, 0x28}, ++{0x300C, 0x00}, ++{0x300D, 0x18}, ++{0x300E, 0x90}, ++{0x300F, 0x97}, ++{0x3010, 0x00}, ++{0x3011, 0x40}, ++{0x3012, 0x21}, ++{0x3013, 0x21}, ++{0x3014, 0x00}, ++{0x3015, 0x20}, ++{0x3016, 0x94}, ++{0x3017, 0x00}, ++{0x3018, 0x00}, ++{0x3019, 0x09}, ++{0x301A, 0x46}, ++{0x301B, 0x28}, ++//{0x3053, ,3}, ++{0x3070, 0xC1}, ++{0x3071, 0x81}, ++{0x3072, 0x29}, ++{0x3073, 0x81}, ++//{0x3370, ,3}, ++//{0x3374, ,3}, ++//{0x3375, ,3}, ++//{0x3376, ,3}, ++//{0x3377, ,3}, ++#if 1 ++{0x3410, 0x90}, ++{0x3411, 0x07}, ++{0x3418, 0x48}, ++{0x3419, 0x04}, ++#else ++{0x3410, 0x80}, ++{0x3411, 0x07}, ++{0x3418, 0x38}, ++{0x3419, 0x04}, +#endif ++//{0x34C0, ,3}, ++//{0x34C1, ,3}, ++//{0x34C2, ,3}, ++//{0x34C3, ,3}, ++//{0x34C4, ,3}, ++//{0x34C5, ,3}, ++//{0x34C6, ,3}, ++//{0x34C7, ,3}, ++//{0x34C8, ,3}, ++//{0x34C9, ,3}, ++//{0x34CA, ,3}, ++//{0x34CB, ,3}, ++//{0x34CC, ,3}, ++//{0x34CD, ,3}, ++//{0x34CE, ,3}, ++//{0x34CF, ,3}, ++{0x3584, 0x00}, ++{0x3586, 0x00}, ++{0x3587, 0x01}, ++{0x3588, 0xE6}, ++{0x3589, 0x00}, ++{0x3590, 0x00}, ++{0x3591, 0x00}, ++{0x3594, 0x40}, ++{0x3598, 0x03}, ++{0x3599, 0x00}, ++{0x359A, 0x80}, ++{0x359B, 0x00}, ++{0x359C, 0x00}, ++{0x359D, 0x01}, ++{0x359E, 0x00}, ++{0x359F, 0x02}, ++{0x35A0, 0x00}, ++{0x35A1, 0x04}, ++{0x35A2, 0x20}, ++{0x35A3, 0x00}, ++{0x35A4, 0x40}, ++{0x35A5, 0x00}, ++{0x35A6, 0x80}, ++{0x35A7, 0x00}, ++{0x35A8, 0x00}, ++{0x35A9, 0x01}, ++{0x35AA, 0x3A}, ++{0x35AB, 0x00}, ++{0x35AC, 0x80}, ++{0x35AD, 0x00}, ++{0x35AE, 0x00}, ++{0x35AF, 0x01}, ++{0x35B0, 0x00}, ++{0x35B1, 0x02}, ++{0x35B2, 0x00}, ++{0x35B3, 0x04}, ++{0x35B4, 0x02}, ++{0x35B5, 0x00}, ++{0x35B6, 0x04}, ++{0x35B7, 0x00}, ++{0x35B8, 0x08}, ++{0x35B9, 0x00}, ++{0x35BA, 0x10}, ++{0x35BB, 0x00}, ++{0x35BC, 0x03}, ++{0x35BD, 0x00}, ++{0x35C8, 0x00}, ++{0x35C9, 0x01}, ++{0x35CA, 0x00}, ++{0x35CB, 0x04}, ++{0x35CC, 0x00}, ++{0x35CD, 0x10}, ++{0x35CE, 0x00}, ++{0x35CF, 0x40}, ++{0x35D0, 0x00}, ++{0x35D1, 0x0C}, ++{0x35D2, 0x00}, ++{0x35D3, 0x0C}, ++{0x35D4, 0x00}, ++{0x35D5, 0x0C}, ++{0x35D6, 0x00}, ++{0x35D7, 0x0C}, ++{0x35D8, 0x00}, ++{0x35D9, 0x00}, ++{0x35DA, 0x08}, ++{0x35DB, 0x00}, ++{0x35DC, 0xD8}, ++{0x35DD, 0x0E}, ++{0x35F0, 0x00}, ++{0x35F1, 0x10}, ++{0x35F2, 0x00}, ++{0x35F3, 0x10}, ++{0x35F4, 0x00}, ++{0x35F5, 0x10}, ++{0x35F6, 0x00}, ++{0x35F7, 0x03}, ++{0x35F8, 0x00}, ++{0x35F9, 0x01}, ++{0x35FA, 0x38}, ++{0x35FB, 0x00}, ++{0x35FC, 0xB3}, ++{0x35FD, 0x01}, ++{0x35FE, 0x00}, ++{0x35FF, 0x00}, ++{0x3600, 0x04}, ++{0x3601, 0x06}, ++{0x3604, 0x03}, ++{0x3605, 0x00}, ++{0x3608, 0x03}, ++{0x3609, 0x00}, ++{0x360C, 0x00}, ++{0x360D, 0x00}, ++{0x3610, 0x10}, ++{0x3611, 0x01}, ++{0x3612, 0x00}, ++{0x3613, 0x00}, ++{0x3614, 0x00}, ++{0x3615, 0x00}, ++{0x361C, 0x00}, ++{0x361D, 0x01}, ++{0x361E, 0x00}, ++{0x361F, 0x01}, ++{0x3620, 0x01}, ++{0x3621, 0x00}, ++{0x3622, 0xB0}, ++{0x3623, 0x04}, ++{0x3624, 0xDC}, ++{0x3625, 0x05}, ++{0x3626, 0x00}, ++{0x3627, 0x01}, ++{0x3628, 0xFF}, ++{0x3629, 0x0F}, ++{0x362A, 0x00}, ++{0x362B, 0x10}, ++{0x362C, 0x00}, ++{0x362D, 0x01}, ++//{0x3630, ,3}, ++//{0x3631, ,3}, ++//{0x3632, ,3}, ++//{0x3633, ,3}, ++//{0x3634, ,3}, ++//{0x3635, ,3}, ++//{0x3636, ,3}, ++//{0x3637, ,3}, ++//{0x3638, ,3}, ++//{0x3639, ,3}, ++//{0x363A, ,3}, ++//{0x363B, ,3}, ++//{0x363C, ,3}, ++//{0x363D, ,3}, ++//{0x363E, ,3}, ++//{0x363F, ,3}, ++{0x36C4, 0x99}, ++{0x36C5, 0x09}, ++{0x36C6, 0x18}, ++{0x36C7, 0x07}, ++{0x36C8, 0x65}, ++{0x36C9, 0x0E}, ++{0x36CC, 0x99}, ++{0x36CD, 0x01}, ++{0x36CE, 0x47}, ++{0x36CF, 0x00}, ++{0x36D0, 0x04}, ++{0x36D1, 0x00}, ++{0x36D4, 0x65}, ++{0x36D5, 0x0E}, ++{0x36D6, 0xA4}, ++{0x36D7, 0x0A}, ++{0x36D8, 0x65}, ++{0x36D9, 0x0E}, ++{0x36DC, 0x65}, ++{0x36DD, 0x0E}, ++{0x36DE, 0xA4}, ++{0x36DF, 0x0A}, ++{0x36E0, 0x65}, ++{0x36E1, 0x0E}, ++{0x36E4, 0x65}, ++{0x36E5, 0x0E}, ++{0x36E6, 0xA4}, ++{0x36E7, 0x0A}, ++{0x36E8, 0x65}, ++{0x36E9, 0x0E}, ++{0x36EE, 0x00}, ++{0x36EF, 0x00}, ++{0x36F0, 0x00}, ++{0x36F1, 0x80}, ++{0x36F8, 0x00}, ++{0x3702, 0x03}, ++{0x3703, 0x04}, ++{0x3704, 0x08}, ++{0x370E, 0x0E}, ++{0x3718, 0x62}, ++{0x3719, 0x4A}, ++{0x371A, 0x38}, ++{0x371B, 0x20}, ++{0x371C, 0x64}, ++{0x371D, 0x42}, ++{0x371E, 0x32}, ++{0x371F, 0x1B}, ++{0x3720, 0x98}, ++{0x3721, 0xA0}, ++{0x3722, 0xA8}, ++{0x3723, 0xB0}, ++{0x3748, 0xA5}, ++{0x3749, 0x9B}, ++{0x374A, 0x91}, ++{0x374B, 0x7D}, ++{0x37C0, 0x00}, ++{0x37C1, 0x00}, ++{0x37C2, 0x00}, ++{0x37C4, 0x00}, ++{0x37C5, 0x00}, ++{0x37C6, 0x00}, ++{0x37C8, 0x00}, ++{0x37C9, 0x00}, ++{0x37CA, 0x00}, ++{0x37CC, 0x00}, ++{0x37CD, 0x00}, ++{0x37CE, 0x00}, ++{0x37D0, 0x00}, ++{0x37D1, 0x00}, ++{0x37D2, 0x00}, ++{0x37D4, 0x00}, ++{0x37D5, 0x00}, ++{0x37D6, 0x00}, ++{0x37D8, 0x00}, ++{0x37D9, 0x00}, ++{0x37DA, 0x00}, ++{0x37DC, 0x00}, ++{0x37DD, 0x00}, ++{0x37DE, 0x00}, ++{0x37E0, 0x00}, ++{0x37E1, 0x00}, ++{0x37E2, 0x00}, ++{0x37E4, 0x00}, ++{0x37E5, 0x00}, ++{0x37E6, 0x00}, ++{0x37E8, 0x00}, ++{0x37E9, 0x00}, ++{0x37EA, 0x00}, ++{0x37EC, 0x00}, ++{0x37ED, 0x00}, ++{0x37EE, 0x00}, ++{0x37F0, 0x00}, ++{0x37F4, 0x00}, ++{0x37F5, 0x1E}, ++{0x37F6, 0x34}, ++{0x37F7, 0x00}, ++{0x37F8, 0xFF}, ++{0x37F9, 0xFF}, ++{0x37FA, 0x03}, ++{0x37FC, 0x00}, ++{0x37FD, 0x00}, ++{0x37FE, 0x04}, ++{0x3800, 0xFF}, ++{0x3801, 0xFF}, ++{0x3802, 0x03}, ++{0x3804, 0x00}, ++{0x3805, 0x00}, ++{0x3806, 0x04}, ++{0x3808, 0x00}, ++{0x3809, 0x00}, ++{0x380A, 0x00}, ++{0x380C, 0x00}, ++{0x380D, 0x00}, ++{0x380E, 0x00}, ++{0x3810, 0x00}, ++{0x3811, 0x00}, ++{0x3812, 0x00}, ++{0x3814, 0x00}, ++{0x3815, 0x00}, ++{0x3816, 0x00}, ++{0x3818, 0x00}, ++{0x3819, 0x00}, ++{0x381A, 0x00}, ++{0x381C, 0x00}, ++{0x381D, 0x00}, ++{0x381E, 0x00}, ++{0x3820, 0x00}, ++{0x3821, 0x00}, ++{0x3822, 0x00}, ++{0x3824, 0x00}, ++{0x3825, 0x00}, ++{0x3826, 0x00}, ++{0x3828, 0x00}, ++{0x3829, 0x00}, ++{0x382A, 0x00}, ++{0x382C, 0x00}, ++{0x382D, 0x00}, ++{0x382E, 0x00}, ++{0x3830, 0x00}, ++{0x3831, 0x00}, ++{0x3832, 0x00}, ++{0x3834, 0x00}, ++{0x3835, 0x00}, ++{0x3836, 0x00}, ++{0x3838, 0x22}, ++{0x3839, 0x00}, ++{0x383A, 0x25}, ++{0x383B, 0x00}, ++{0x383C, 0x1A}, ++{0x383D, 0x00}, ++{0x383E, 0x26}, ++{0x383F, 0x00}, ++{0x3840, 0x07}, ++{0x3841, 0x00}, ++{0x3842, 0x06}, ++{0x3843, 0x00}, ++{0x3844, 0x03}, ++{0x3845, 0x00}, ++{0x3846, 0x02}, ++{0x3847, 0x00}, ++{0x3848, 0xFB}, ++{0x3849, 0xFF}, ++{0x384A, 0xFF}, ++{0x384B, 0xFF}, ++{0x384C, 0xF3}, ++{0x384D, 0xFF}, ++{0x384E, 0xF2}, ++{0x384F, 0xFF}, ++{0x3850, 0xFF}, ++{0x3851, 0x0F}, ++{0x3852, 0x00}, ++{0x3853, 0x10}, ++{0x3854, 0xFF}, ++{0x3855, 0x0F}, ++{0x3856, 0x00}, ++{0x3857, 0x10}, ++{0x3858, 0xFF}, ++{0x3859, 0x0F}, ++{0x385A, 0x00}, ++{0x385B, 0x10}, ++{0x385C, 0x02}, ++{0x385D, 0x00}, ++{0x385E, 0x06}, ++{0x385F, 0x00}, ++{0x3860, 0x06}, ++{0x3861, 0x00}, ++{0x3862, 0x08}, ++{0x3863, 0x00}, ++{0x3864, 0x02}, ++{0x3865, 0x00}, ++{0x38A0, 0x01}, ++{0x38A1, 0x01}, ++{0x38A2, 0x00}, ++{0x38A3, 0x01}, ++{0x38A4, 0x07}, ++{0x38A5, 0x00}, ++{0x38A6, 0x04}, ++{0x38A7, 0x05}, ++{0x38A8, 0x00}, ++{0x38A9, 0x00}, ++{0x38AC, 0x00}, ++{0x38AD, 0x00}, ++{0x38AE, 0x01}, ++{0x38B0, 0x02}, ++{0x38B2, 0x22}, ++{0x38B3, 0x00}, ++{0x38B4, 0x17}, ++{0x38B5, 0x00}, ++{0x38B6, 0x11}, ++{0x38B7, 0x00}, ++{0x38B8, 0x0E}, ++{0x38B9, 0x00}, ++{0x38BA, 0x2A}, ++{0x38BB, 0x00}, ++{0x38BC, 0x1C}, ++{0x38BD, 0x00}, ++{0x38BE, 0x14}, ++{0x38BF, 0x00}, ++{0x38C0, 0x10}, ++{0x38C1, 0x00}, ++{0x38C2, 0x31}, ++{0x38C3, 0x00}, ++{0x38C4, 0x21}, ++{0x38C5, 0x00}, ++{0x38C6, 0x18}, ++{0x38C7, 0x00}, ++{0x38C8, 0x12}, ++{0x38C9, 0x00}, ++{0x38CA, 0x3C}, ++{0x38CB, 0x00}, ++{0x38CC, 0x29}, ++{0x38CD, 0x00}, ++{0x38CE, 0x1D}, ++{0x38CF, 0x00}, ++{0x38D0, 0x15}, ++{0x38D1, 0x00}, ++{0x38D2, 0x4E}, ++{0x38D3, 0x00}, ++{0x38D4, 0x35}, ++{0x38D5, 0x00}, ++{0x38D6, 0x26}, ++{0x38D7, 0x00}, ++{0x38D8, 0x1A}, ++{0x38D9, 0x00}, ++{0x38DA, 0x69}, ++{0x38DB, 0x00}, ++{0x38DC, 0x48}, ++{0x38DD, 0x00}, ++{0x38DE, 0x33}, ++{0x38DF, 0x00}, ++{0x38E0, 0x22}, ++{0x38E1, 0x00}, ++{0x38E2, 0x93}, ++{0x38E3, 0x00}, ++{0x38E4, 0x64}, ++{0x38E5, 0x00}, ++{0x38E6, 0x48}, ++{0x38E7, 0x00}, ++{0x38E8, 0x30}, ++{0x38E9, 0x00}, ++{0x38EA, 0xD3}, ++{0x38EB, 0x00}, ++{0x38EC, 0x90}, ++{0x38ED, 0x00}, ++{0x38EE, 0x69}, ++{0x38EF, 0x00}, ++{0x38F0, 0x49}, ++{0x38F1, 0x00}, ++{0x38F2, 0x39}, ++{0x38F3, 0x01}, ++{0x38F4, 0xD5}, ++{0x38F5, 0x00}, ++{0x38F6, 0x9F}, ++{0x38F7, 0x00}, ++{0x38F8, 0x75}, ++{0x38F9, 0x00}, ++{0x38FA, 0x00}, ++{0x38FB, 0x01}, ++{0x38FC, 0x00}, ++{0x38FD, 0x01}, ++{0x38FE, 0x00}, ++{0x38FF, 0x01}, ++{0x3900, 0x00}, ++{0x3901, 0x01}, ++{0x3902, 0x60}, ++{0x3903, 0x00}, ++{0x3904, 0x25}, ++{0x3905, 0x00}, ++{0x3906, 0x18}, ++{0x3907, 0x00}, ++{0x3908, 0x10}, ++{0x3909, 0x00}, ++{0x390A, 0xFF}, ++{0x390B, 0x00}, ++{0x390C, 0xD5}, ++{0x390D, 0x00}, ++{0x390E, 0xAA}, ++{0x390F, 0x00}, ++{0x3910, 0x85}, ++{0x3911, 0x00}, ++{0x3912, 0xFF}, ++{0x3913, 0x00}, ++{0x3914, 0xD5}, ++{0x3915, 0x00}, ++{0x3916, 0xAA}, ++{0x3917, 0x00}, ++{0x3918, 0x85}, ++{0x3919, 0x00}, ++{0x391A, 0xFF}, ++{0x391B, 0x00}, ++{0x391C, 0xD5}, ++{0x391D, 0x00}, ++{0x391E, 0xAA}, ++{0x391F, 0x00}, ++{0x3920, 0x85}, ++{0x3921, 0x00}, ++{0x3922, 0x40}, ++{0x3923, 0x00}, ++{0x3924, 0x40}, ++{0x3925, 0x00}, ++{0x3926, 0x40}, ++{0x3927, 0x00}, ++{0x3928, 0x40}, ++{0x3929, 0x00}, ++{0x392A, 0x80}, ++{0x392B, 0x00}, ++{0x392C, 0x80}, ++{0x392D, 0x00}, ++{0x392E, 0x80}, ++{0x392F, 0x00}, ++{0x3930, 0x80}, ++{0x3931, 0x00}, ++{0x3932, 0x4C}, ++{0x3933, 0x4C}, ++{0x3934, 0x4C}, ++{0x3940, 0x01}, ++{0x3941, 0x01}, ++{0x3942, 0x00}, ++{0x3943, 0x01}, ++{0x3944, 0x07}, ++{0x3945, 0x00}, ++{0x3946, 0x04}, ++{0x3947, 0x05}, ++{0x3948, 0x00}, ++{0x3949, 0x00}, ++{0x394C, 0x00}, ++{0x394D, 0x00}, ++{0x394E, 0x01}, ++{0x3950, 0x03}, ++{0x3952, 0x14}, ++{0x3953, 0x00}, ++{0x3954, 0x0F}, ++{0x3955, 0x00}, ++{0x3956, 0x0E}, ++{0x3957, 0x00}, ++{0x3958, 0x0E}, ++{0x3959, 0x00}, ++{0x395A, 0x19}, ++{0x395B, 0x00}, ++{0x395C, 0x11}, ++{0x395D, 0x00}, ++{0x395E, 0x0F}, ++{0x395F, 0x00}, ++{0x3960, 0x0E}, ++{0x3961, 0x00}, ++{0x3962, 0x1C}, ++{0x3963, 0x00}, ++{0x3964, 0x13}, ++{0x3965, 0x00}, ++{0x3966, 0x0F}, ++{0x3967, 0x00}, ++{0x3968, 0x0E}, ++{0x3969, 0x00}, ++{0x396A, 0x23}, ++{0x396B, 0x00}, ++{0x396C, 0x15}, ++{0x396D, 0x00}, ++{0x396E, 0x11}, ++{0x396F, 0x00}, ++{0x3970, 0x0E}, ++{0x3971, 0x00}, ++{0x3972, 0x2E}, ++{0x3973, 0x00}, ++{0x3974, 0x1A}, ++{0x3975, 0x00}, ++{0x3976, 0x14}, ++{0x3977, 0x00}, ++{0x3978, 0x0F}, ++{0x3979, 0x00}, ++{0x397A, 0x3E}, ++{0x397B, 0x00}, ++{0x397C, 0x23}, ++{0x397D, 0x00}, ++{0x397E, 0x1A}, ++{0x397F, 0x00}, ++{0x3980, 0x12}, ++{0x3981, 0x00}, ++{0x3982, 0x56}, ++{0x3983, 0x00}, ++{0x3984, 0x31}, ++{0x3985, 0x00}, ++{0x3986, 0x25}, ++{0x3987, 0x00}, ++{0x3988, 0x1A}, ++{0x3989, 0x00}, ++{0x398A, 0x7B}, ++{0x398B, 0x00}, ++{0x398C, 0x49}, ++{0x398D, 0x00}, ++{0x398E, 0x39}, ++{0x398F, 0x00}, ++{0x3990, 0x2C}, ++{0x3991, 0x00}, ++{0x3992, 0xB4}, ++{0x3993, 0x00}, ++{0x3994, 0x75}, ++{0x3995, 0x00}, ++{0x3996, 0x61}, ++{0x3997, 0x00}, ++{0x3998, 0x53}, ++{0x3999, 0x00}, ++{0x399A, 0x00}, ++{0x399B, 0x01}, ++{0x399C, 0x00}, ++{0x399D, 0x01}, ++{0x399E, 0x00}, ++{0x399F, 0x01}, ++{0x39A0, 0x00}, ++{0x39A1, 0x01}, ++{0x39A2, 0x60}, ++{0x39A3, 0x00}, ++{0x39A4, 0x20}, ++{0x39A5, 0x00}, ++{0x39A6, 0x15}, ++{0x39A7, 0x00}, ++{0x39A8, 0x10}, ++{0x39A9, 0x00}, ++{0x39AA, 0xFF}, ++{0x39AB, 0x00}, ++{0x39AC, 0xD5}, ++{0x39AD, 0x00}, ++{0x39AE, 0xAA}, ++{0x39AF, 0x00}, ++{0x39B0, 0x85}, ++{0x39B1, 0x00}, ++{0x39B2, 0xFF}, ++{0x39B3, 0x00}, ++{0x39B4, 0xD5}, ++{0x39B5, 0x00}, ++{0x39B6, 0xAA}, ++{0x39B7, 0x00}, ++{0x39B8, 0x85}, ++{0x39B9, 0x00}, ++{0x39BA, 0xFF}, ++{0x39BB, 0x00}, ++{0x39BC, 0xD5}, ++{0x39BD, 0x00}, ++{0x39BE, 0xAA}, ++{0x39BF, 0x00}, ++{0x39C0, 0x85}, ++{0x39C1, 0x00}, ++{0x39C2, 0x40}, ++{0x39C3, 0x00}, ++{0x39C4, 0x40}, ++{0x39C5, 0x00}, ++{0x39C6, 0x40}, ++{0x39C7, 0x00}, ++{0x39C8, 0x40}, ++{0x39C9, 0x00}, ++{0x39CA, 0x80}, ++{0x39CB, 0x00}, ++{0x39CC, 0x80}, ++{0x39CD, 0x00}, ++{0x39CE, 0x80}, ++{0x39CF, 0x00}, ++{0x39D0, 0x80}, ++{0x39D1, 0x00}, ++{0x39D2, 0x4C}, ++{0x39D3, 0x4C}, ++{0x39D4, 0x4C}, ++{0x39E0, 0x01}, ++{0x39E1, 0x00}, ++{0x39E4, 0x40}, ++{0x39E5, 0x01}, ++{0x39E6, 0x01}, ++{0x39E8, 0x00}, ++{0x39E9, 0x01}, ++{0x39EA, 0x00}, ++{0x39EB, 0x00}, ++{0x39EC, 0x01}, ++{0x39ED, 0x00}, ++{0x39EE, 0x01}, ++{0x39F0, 0x03}, ++{0x39F1, 0x04}, ++{0x39F2, 0x0E}, ++{0x39F4, 0x1C}, ++{0x39F5, 0x00}, ++{0x39F6, 0x13}, ++{0x39F7, 0x00}, ++{0x39F8, 0x0D}, ++{0x39F9, 0x00}, ++{0x39FA, 0x07}, ++{0x39FB, 0x00}, ++{0x39FC, 0x38}, ++{0x39FD, 0x00}, ++{0x39FE, 0x1C}, ++{0x39FF, 0x00}, ++{0x3A00, 0x11}, ++{0x3A01, 0x00}, ++{0x3A02, 0x08}, ++{0x3A03, 0x00}, ++{0x3A04, 0x4A}, ++{0x3A05, 0x00}, ++{0x3A06, 0x23}, ++{0x3A07, 0x00}, ++{0x3A08, 0x15}, ++{0x3A09, 0x00}, ++{0x3A0A, 0x09}, ++{0x3A0B, 0x00}, ++{0x3A0C, 0x65}, ++{0x3A0D, 0x00}, ++{0x3A0E, 0x2D}, ++{0x3A0F, 0x00}, ++{0x3A10, 0x1A}, ++{0x3A11, 0x00}, ++{0x3A12, 0x0B}, ++{0x3A13, 0x00}, ++{0x3A14, 0x8D}, ++{0x3A15, 0x00}, ++{0x3A16, 0x3D}, ++{0x3A17, 0x00}, ++{0x3A18, 0x23}, ++{0x3A19, 0x00}, ++{0x3A1A, 0x0E}, ++{0x3A1B, 0x00}, ++{0x3A1C, 0xC5}, ++{0x3A1D, 0x00}, ++{0x3A1E, 0x55}, ++{0x3A1F, 0x00}, ++{0x3A20, 0x30}, ++{0x3A21, 0x00}, ++{0x3A22, 0x13}, ++{0x3A23, 0x00}, ++{0x3A24, 0x16}, ++{0x3A25, 0x01}, ++{0x3A26, 0x76}, ++{0x3A27, 0x00}, ++{0x3A28, 0x42}, ++{0x3A29, 0x00}, ++{0x3A2A, 0x1A}, ++{0x3A2B, 0x00}, ++{0x3A2C, 0x88}, ++{0x3A2D, 0x01}, ++{0x3A2E, 0xA7}, ++{0x3A2F, 0x00}, ++{0x3A30, 0x5D}, ++{0x3A31, 0x00}, ++{0x3A32, 0x24}, ++{0x3A33, 0x00}, ++{0x3A34, 0x2A}, ++{0x3A35, 0x02}, ++{0x3A36, 0xEB}, ++{0x3A37, 0x00}, ++{0x3A38, 0x83}, ++{0x3A39, 0x00}, ++{0x3A3A, 0x32}, ++{0x3A3B, 0x00}, ++{0x3A3C, 0x00}, ++{0x3A3D, 0x01}, ++{0x3A3E, 0x00}, ++{0x3A3F, 0x01}, ++{0x3A40, 0x00}, ++{0x3A41, 0x01}, ++{0x3A42, 0x00}, ++{0x3A43, 0x01}, ++{0x3A44, 0x70}, ++{0x3A45, 0x00}, ++{0x3A46, 0x25}, ++{0x3A47, 0x00}, ++{0x3A48, 0x18}, ++{0x3A49, 0x00}, ++{0x3A4A, 0x10}, ++{0x3A4B, 0x00}, ++{0x3A4C, 0xFF}, ++{0x3A4D, 0x00}, ++{0x3A4E, 0xD5}, ++{0x3A4F, 0x00}, ++{0x3A50, 0xAA}, ++{0x3A51, 0x00}, ++{0x3A52, 0x85}, ++{0x3A53, 0x00}, ++{0x3A54, 0xFF}, ++{0x3A55, 0x00}, ++{0x3A56, 0xD5}, ++{0x3A57, 0x00}, ++{0x3A58, 0xAA}, ++{0x3A59, 0x00}, ++{0x3A5A, 0x85}, ++{0x3A5B, 0x00}, ++{0x3A5C, 0xFF}, ++{0x3A5D, 0x00}, ++{0x3A5E, 0xD5}, ++{0x3A5F, 0x00}, ++{0x3A60, 0xAA}, ++{0x3A61, 0x00}, ++{0x3A62, 0x85}, ++{0x3A63, 0x00}, ++{0x3A64, 0x1C}, ++{0x3A65, 0x00}, ++{0x3A66, 0x13}, ++{0x3A67, 0x00}, ++{0x3A68, 0x0D}, ++{0x3A69, 0x00}, ++{0x3A6A, 0x07}, ++{0x3A6B, 0x00}, ++{0x3A6C, 0x0D}, ++{0x3A6D, 0x00}, ++{0x3A6E, 0x0B}, ++{0x3A6F, 0x00}, ++{0x3A70, 0x06}, ++{0x3A71, 0x00}, ++{0x3A72, 0x05}, ++{0x3A73, 0x00}, ++{0x3A74, 0x19}, ++{0x3A75, 0x00}, ++{0x3A76, 0x14}, ++{0x3A77, 0x00}, ++{0x3A78, 0x0F}, ++{0x3A79, 0x00}, ++{0x3A7A, 0x0A}, ++{0x3A7B, 0x00}, ++{0x3A7C, 0x80}, ++{0x3A7D, 0x00}, ++{0x3A7E, 0x80}, ++{0x3A7F, 0x00}, ++{0x3A80, 0x80}, ++{0x3A81, 0x00}, ++{0x3A82, 0x80}, ++{0x3A83, 0x00}, ++{0x3A84, 0x08}, ++{0x3A85, 0x00}, ++{0x3A86, 0x05}, ++{0x3A87, 0x00}, ++{0x3A88, 0x04}, ++{0x3A89, 0x00}, ++{0x3A8A, 0x03}, ++{0x3A8B, 0x00}, ++{0x3A8C, 0xCD}, ++{0x3A8D, 0x00}, ++{0x3A8E, 0xAA}, ++{0x3A8F, 0x00}, ++{0x3A90, 0x8C}, ++{0x3A91, 0x00}, ++{0x3A92, 0x64}, ++{0x3A93, 0x00}, ++{0x3A94, 0xCD}, ++{0x3A95, 0x00}, ++{0x3A96, 0xAA}, ++{0x3A97, 0x00}, ++{0x3A98, 0x8C}, ++{0x3A99, 0x00}, ++{0x3A9A, 0x64}, ++{0x3A9B, 0x00}, ++{0x3A9C, 0x08}, ++{0x3A9D, 0x10}, ++{0x3A9E, 0x4C}, ++{0x3A9F, 0x4C}, ++{0x3AA0, 0x4C}, ++{0x3AA1, 0x04}, ++{0x3AA2, 0x05}, ++{0x3AC0, 0x01}, ++{0x3AC4, 0x81}, ++{0x3AC5, 0x00}, ++{0x3AC6, 0x00}, ++{0x3AC7, 0x00}, ++{0x3AC8, 0x00}, ++{0x3AC9, 0x00}, ++{0x3ACA, 0x00}, ++{0x3ACB, 0x00}, ++{0x3ACC, 0x02}, ++{0x3ACD, 0x00}, ++{0x3ACE, 0x81}, ++{0x3ACF, 0x00}, ++{0x3AD0, 0x00}, ++{0x3AD1, 0x00}, ++{0x3AD2, 0xFD}, ++{0x3AD3, 0x03}, ++{0x3AD4, 0x02}, ++{0x3AD5, 0x00}, ++{0x3AD6, 0x00}, ++{0x3AD7, 0x00}, ++{0x3AD8, 0x81}, ++{0x3AD9, 0x00}, ++{0x3ADA, 0xFD}, ++{0x3ADB, 0x03}, ++{0x3ADC, 0xFF}, ++{0x3ADD, 0x03}, ++{0x3ADE, 0x01}, ++{0x3ADF, 0x00}, ++{0x3AE0, 0x01}, ++{0x3AE1, 0x00}, ++{0x3AE2, 0x7E}, ++{0x3AE3, 0x00}, ++{0x3AF4, 0x00}, ++{0x3AF6, 0x40}, ++{0x3AF7, 0x1E}, ++{0x3AF8, 0x01}, ++{0x3AFA, 0x63}, ++{0x3AFB, 0x09}, ++{0x3AFC, 0x11}, ++{0x3AFD, 0x09}, ++{0x3AFE, 0x00}, ++{0x3AFF, 0x00}, ++{0x3B00, 0x00}, ++{0x3B01, 0x00}, ++{0x3B02, 0x84}, ++{0x3B03, 0x06}, ++{0x3B04, 0x30}, ++{0x3B05, 0x06}, ++{0x3B06, 0x00}, ++{0x3B07, 0x00}, ++{0x3B08, 0x00}, ++{0x3B09, 0x00}, ++{0x3B0A, 0x00}, ++{0x3B0B, 0x00}, ++{0x3B0C, 0x00}, ++{0x3B0D, 0x00}, ++{0x3B0E, 0x00}, ++{0x3B0F, 0x00}, ++{0x3B10, 0x00}, ++{0x3B11, 0x00}, ++{0x3B12, 0x00}, ++{0x3B13, 0x00}, ++{0x3B14, 0x00}, ++{0x3B15, 0x00}, ++{0x3B16, 0x00}, ++{0x3B17, 0x00}, ++{0x3B18, 0x00}, ++{0x3B19, 0x00}, ++{0x3B1A, 0x00}, ++{0x3B1B, 0x00}, ++{0x3B1C, 0x00}, ++{0x3B1D, 0x00}, ++{0x3B1E, 0x00}, ++{0x3B1F, 0x00}, ++{0x3B20, 0x00}, ++{0x3B21, 0x00}, ++{0x3B22, 0x00}, ++{0x3B23, 0x00}, ++{0x3B24, 0x00}, ++{0x3B25, 0x00}, ++{0x3B26, 0x00}, ++{0x3B27, 0x00}, ++{0x3B28, 0x00}, ++{0x3B29, 0x00}, ++{0x3B2A, 0x00}, ++{0x3B2C, 0x00}, ++{0x3B2E, 0x00}, ++{0x3B30, 0x00}, ++{0x3B32, 0x0C}, ++{0x4000, 0xD1}, ++{0x4001, 0xC0}, ++{0x4002, 0xC0}, ++{0x4003, 0xB8}, ++{0x4004, 0xC0}, ++{0x4005, 0xB8}, ++{0x4006, 0xB9}, ++{0x4007, 0xB7}, ++{0x4008, 0xB0}, ++{0x4009, 0xAB}, ++{0x400A, 0xAC}, ++{0x400B, 0xAB}, ++{0x400C, 0xA8}, ++{0x400D, 0xA6}, ++{0x400E, 0xA6}, ++{0x400F, 0xA5}, ++{0x4010, 0xA2}, ++{0x4011, 0xA0}, ++{0x4012, 0xA0}, ++{0x4013, 0x9F}, ++{0x4014, 0xA4}, ++{0x4015, 0xA2}, ++{0x4016, 0xA2}, ++{0x4017, 0x9C}, ++{0x4018, 0xA8}, ++{0x4019, 0xA6}, ++{0x401A, 0xA8}, ++{0x401B, 0xAA}, ++{0x401C, 0xB0}, ++{0x401D, 0xAE}, ++{0x401E, 0xAE}, ++{0x401F, 0xAE}, ++{0x4020, 0xBA}, ++{0x4021, 0xAE}, ++{0x4022, 0xAF}, ++{0x4023, 0xAE}, ++{0x4024, 0xC6}, ++{0x4025, 0xBD}, ++{0x4026, 0xBD}, ++{0x4027, 0xBA}, ++{0x4028, 0xB0}, ++{0x4029, 0xA9}, ++{0x402A, 0xAA}, ++{0x402B, 0xA8}, ++{0x402C, 0x9F}, ++{0x402D, 0x9C}, ++{0x402E, 0x9C}, ++{0x402F, 0x9B}, ++{0x4030, 0x93}, ++{0x4031, 0x91}, ++{0x4032, 0x92}, ++{0x4033, 0x91}, ++{0x4034, 0x8D}, ++{0x4035, 0x8C}, ++{0x4036, 0x8C}, ++{0x4037, 0x8C}, ++{0x4038, 0x8F}, ++{0x4039, 0x8E}, ++{0x403A, 0x8E}, ++{0x403B, 0x8E}, ++{0x403C, 0x98}, ++{0x403D, 0x96}, ++{0x403E, 0x96}, ++{0x403F, 0x95}, ++{0x4040, 0xA4}, ++{0x4041, 0xA0}, ++{0x4042, 0xA0}, ++{0x4043, 0x9E}, ++{0x4044, 0xB3}, ++{0x4045, 0xAE}, ++{0x4046, 0xAF}, ++{0x4047, 0xAB}, ++{0x4048, 0xC2}, ++{0x4049, 0xB7}, ++{0x404A, 0xB8}, ++{0x404B, 0xB5}, ++{0x404C, 0xAB}, ++{0x404D, 0xA4}, ++{0x404E, 0xA5}, ++{0x404F, 0xA3}, ++{0x4050, 0x99}, ++{0x4051, 0x96}, ++{0x4052, 0x96}, ++{0x4053, 0x96}, ++{0x4054, 0x8B}, ++{0x4055, 0x8A}, ++{0x4056, 0x8A}, ++{0x4057, 0x8A}, ++{0x4058, 0x82}, ++{0x4059, 0x81}, ++{0x405A, 0x81}, ++{0x405B, 0x81}, ++{0x405C, 0x85}, ++{0x405D, 0x86}, ++{0x405E, 0x85}, ++{0x405F, 0x85}, ++{0x4060, 0x90}, ++{0x4061, 0x90}, ++{0x4062, 0x8F}, ++{0x4063, 0x8F}, ++{0x4064, 0x9D}, ++{0x4065, 0x9B}, ++{0x4066, 0x9B}, ++{0x4067, 0x9A}, ++{0x4068, 0xAF}, ++{0x4069, 0xAA}, ++{0x406A, 0xAC}, ++{0x406B, 0xAA}, ++{0x406C, 0xC2}, ++{0x406D, 0xB7}, ++{0x406E, 0xB8}, ++{0x406F, 0xB5}, ++{0x4070, 0xAB}, ++{0x4071, 0xA4}, ++{0x4072, 0xA4}, ++{0x4073, 0xA3}, ++{0x4074, 0x99}, ++{0x4075, 0x96}, ++{0x4076, 0x96}, ++{0x4077, 0x96}, ++{0x4078, 0x8B}, ++{0x4079, 0x8A}, ++{0x407A, 0x8A}, ++{0x407B, 0x8A}, ++{0x407C, 0x82}, ++{0x407D, 0x82}, ++{0x407E, 0x82}, ++{0x407F, 0x82}, ++{0x4080, 0x85}, ++{0x4081, 0x86}, ++{0x4082, 0x86}, ++{0x4083, 0x86}, ++{0x4084, 0x90}, ++{0x4085, 0x90}, ++{0x4086, 0x8F}, ++{0x4087, 0x8F}, ++{0x4088, 0x9D}, ++{0x4089, 0x9B}, ++{0x408A, 0x9B}, ++{0x408B, 0x99}, ++{0x408C, 0xAE}, ++{0x408D, 0xAA}, ++{0x408E, 0xAA}, ++{0x408F, 0xA7}, ++{0x4090, 0xC7}, ++{0x4091, 0xBA}, ++{0x4092, 0xBC}, ++{0x4093, 0xB9}, ++{0x4094, 0xB1}, ++{0x4095, 0xA8}, ++{0x4096, 0xA8}, ++{0x4097, 0xA7}, ++{0x4098, 0x9F}, ++{0x4099, 0x9B}, ++{0x409A, 0x9B}, ++{0x409B, 0x9B}, ++{0x409C, 0x93}, ++{0x409D, 0x91}, ++{0x409E, 0x91}, ++{0x409F, 0x91}, ++{0x40A0, 0x8D}, ++{0x40A1, 0x8C}, ++{0x40A2, 0x8C}, ++{0x40A3, 0x8C}, ++{0x40A4, 0x8E}, ++{0x40A5, 0x8E}, ++{0x40A6, 0x8D}, ++{0x40A7, 0x8D}, ++{0x40A8, 0x96}, ++{0x40A9, 0x95}, ++{0x40AA, 0x95}, ++{0x40AB, 0x94}, ++{0x40AC, 0xA2}, ++{0x40AD, 0x9F}, ++{0x40AE, 0x9F}, ++{0x40AF, 0x9D}, ++{0x40B0, 0xB1}, ++{0x40B1, 0xAC}, ++{0x40B2, 0xAB}, ++{0x40B3, 0xAA}, ++{0x40B4, 0xD3}, ++{0x40B5, 0xBC}, ++{0x40B6, 0xBD}, ++{0x40B7, 0xBC}, ++{0x40B8, 0xC1}, ++{0x40B9, 0xB7}, ++{0x40BA, 0xB7}, ++{0x40BB, 0xB5}, ++{0x40BC, 0xB0}, ++{0x40BD, 0xAA}, ++{0x40BE, 0xAA}, ++{0x40BF, 0xAA}, ++{0x40C0, 0xA8}, ++{0x40C1, 0xA4}, ++{0x40C2, 0xA4}, ++{0x40C3, 0xA4}, ++{0x40C4, 0xA2}, ++{0x40C5, 0x9F}, ++{0x40C6, 0x9F}, ++{0x40C7, 0x9F}, ++{0x40C8, 0xA3}, ++{0x40C9, 0xA0}, ++{0x40CA, 0xA0}, ++{0x40CB, 0xA0}, ++{0x40CC, 0xA6}, ++{0x40CD, 0xA3}, ++{0x40CE, 0xA3}, ++{0x40CF, 0xA2}, ++{0x40D0, 0xAF}, ++{0x40D1, 0xAB}, ++{0x40D2, 0xAA}, ++{0x40D3, 0xA8}, ++{0x40D4, 0xBA}, ++{0x40D5, 0xAE}, ++{0x40D6, 0xAE}, ++{0x40D7, 0xAB}, ++{0x4100, 0xBD}, ++{0x4101, 0xBA}, ++{0x4102, 0xBD}, ++{0x4103, 0xB7}, ++{0x4104, 0xB7}, ++{0x4105, 0xB7}, ++{0x4106, 0xB8}, ++{0x4107, 0xB5}, ++{0x4108, 0xAB}, ++{0x4109, 0xAA}, ++{0x410A, 0xAC}, ++{0x410B, 0xAB}, ++{0x410C, 0xA4}, ++{0x410D, 0xA5}, ++{0x410E, 0xA5}, ++{0x410F, 0xA4}, ++{0x4110, 0x9F}, ++{0x4111, 0xA0}, ++{0x4112, 0xA0}, ++{0x4113, 0x9F}, ++{0x4114, 0xA0}, ++{0x4115, 0xA0}, ++{0x4116, 0xA0}, ++{0x4117, 0x9F}, ++{0x4118, 0xA1}, ++{0x4119, 0xA1}, ++{0x411A, 0xA1}, ++{0x411B, 0xA0}, ++{0x411C, 0xA7}, ++{0x411D, 0xA6}, ++{0x411E, 0xA6}, ++{0x411F, 0xA6}, ++{0x4120, 0xA7}, ++{0x4121, 0xA6}, ++{0x4122, 0xA6}, ++{0x4123, 0xA3}, ++{0x4124, 0xB9}, ++{0x4125, 0xB9}, ++{0x4126, 0xBA}, ++{0x4127, 0xB8}, ++{0x4128, 0xA6}, ++{0x4129, 0xA7}, ++{0x412A, 0xA7}, ++{0x412B, 0xA6}, ++{0x412C, 0x9B}, ++{0x412D, 0x9B}, ++{0x412E, 0x9B}, ++{0x412F, 0x9B}, ++{0x4130, 0x91}, ++{0x4131, 0x92}, ++{0x4132, 0x92}, ++{0x4133, 0x91}, ++{0x4134, 0x8C}, ++{0x4135, 0x8C}, ++{0x4136, 0x8C}, ++{0x4137, 0x8C}, ++{0x4138, 0x8D}, ++{0x4139, 0x8D}, ++{0x413A, 0x8D}, ++{0x413B, 0x8D}, ++{0x413C, 0x93}, ++{0x413D, 0x93}, ++{0x413E, 0x93}, ++{0x413F, 0x92}, ++{0x4140, 0x9A}, ++{0x4141, 0x9A}, ++{0x4142, 0x9A}, ++{0x4143, 0x99}, ++{0x4144, 0xA7}, ++{0x4145, 0xA5}, ++{0x4146, 0xA6}, ++{0x4147, 0xA6}, ++{0x4148, 0xB8}, ++{0x4149, 0xB4}, ++{0x414A, 0xB4}, ++{0x414B, 0xB3}, ++{0x414C, 0xA3}, ++{0x414D, 0xA2}, ++{0x414E, 0xA3}, ++{0x414F, 0xA2}, ++{0x4150, 0x96}, ++{0x4151, 0x96}, ++{0x4152, 0x96}, ++{0x4153, 0x96}, ++{0x4154, 0x8A}, ++{0x4155, 0x8A}, ++{0x4156, 0x8A}, ++{0x4157, 0x8A}, ++{0x4158, 0x82}, ++{0x4159, 0x82}, ++{0x415A, 0x82}, ++{0x415B, 0x82}, ++{0x415C, 0x84}, ++{0x415D, 0x85}, ++{0x415E, 0x84}, ++{0x415F, 0x84}, ++{0x4160, 0x8D}, ++{0x4161, 0x8D}, ++{0x4162, 0x8D}, ++{0x4163, 0x8D}, ++{0x4164, 0x96}, ++{0x4165, 0x96}, ++{0x4166, 0x96}, ++{0x4167, 0x95}, ++{0x4168, 0xA5}, ++{0x4169, 0xA2}, ++{0x416A, 0xA3}, ++{0x416B, 0xA2}, ++{0x416C, 0xB7}, ++{0x416D, 0xB3}, ++{0x416E, 0xB5}, ++{0x416F, 0xB4}, ++{0x4170, 0xA4}, ++{0x4171, 0xA2}, ++{0x4172, 0xA3}, ++{0x4173, 0xA2}, ++{0x4174, 0x97}, ++{0x4175, 0x96}, ++{0x4176, 0x96}, ++{0x4177, 0x96}, ++{0x4178, 0x8B}, ++{0x4179, 0x8A}, ++{0x417A, 0x8A}, ++{0x417B, 0x8A}, ++{0x417C, 0x81}, ++{0x417D, 0x81}, ++{0x417E, 0x81}, ++{0x417F, 0x81}, ++{0x4180, 0x84}, ++{0x4181, 0x84}, ++{0x4182, 0x84}, ++{0x4183, 0x84}, ++{0x4184, 0x8C}, ++{0x4185, 0x8D}, ++{0x4186, 0x8D}, ++{0x4187, 0x8D}, ++{0x4188, 0x95}, ++{0x4189, 0x96}, ++{0x418A, 0x96}, ++{0x418B, 0x95}, ++{0x418C, 0xA1}, ++{0x418D, 0xA1}, ++{0x418E, 0xA1}, ++{0x418F, 0xA0}, ++{0x4190, 0xBC}, ++{0x4191, 0xB8}, ++{0x4192, 0xB8}, ++{0x4193, 0xB9}, ++{0x4194, 0xA8}, ++{0x4195, 0xA5}, ++{0x4196, 0xA6}, ++{0x4197, 0xA5}, ++{0x4198, 0x9C}, ++{0x4199, 0x9A}, ++{0x419A, 0x9A}, ++{0x419B, 0x9A}, ++{0x419C, 0x91}, ++{0x419D, 0x91}, ++{0x419E, 0x91}, ++{0x419F, 0x91}, ++{0x41A0, 0x8B}, ++{0x41A1, 0x8B}, ++{0x41A2, 0x8B}, ++{0x41A3, 0x8B}, ++{0x41A4, 0x8C}, ++{0x41A5, 0x8C}, ++{0x41A6, 0x8C}, ++{0x41A7, 0x8C}, ++{0x41A8, 0x91}, ++{0x41A9, 0x92}, ++{0x41AA, 0x91}, ++{0x41AB, 0x91}, ++{0x41AC, 0x98}, ++{0x41AD, 0x99}, ++{0x41AE, 0x99}, ++{0x41AF, 0x98}, ++{0x41B0, 0xA3}, ++{0x41B1, 0xA3}, ++{0x41B2, 0xA3}, ++{0x41B3, 0xA2}, ++{0x41B4, 0xC1}, ++{0x41B5, 0xB8}, ++{0x41B6, 0xB9}, ++{0x41B7, 0xBA}, ++{0x41B8, 0xB8}, ++{0x41B9, 0xB4}, ++{0x41BA, 0xB4}, ++{0x41BB, 0xB4}, ++{0x41BC, 0xAA}, ++{0x41BD, 0xA7}, ++{0x41BE, 0xA7}, ++{0x41BF, 0xA8}, ++{0x41C0, 0xA4}, ++{0x41C1, 0xA2}, ++{0x41C2, 0xA2}, ++{0x41C3, 0xA3}, ++{0x41C4, 0x9E}, ++{0x41C5, 0x9D}, ++{0x41C6, 0x9D}, ++{0x41C7, 0x9D}, ++{0x41C8, 0x9E}, ++{0x41C9, 0x9D}, ++{0x41CA, 0x9D}, ++{0x41CB, 0x9D}, ++{0x41CC, 0x9E}, ++{0x41CD, 0x9E}, ++{0x41CE, 0x9E}, ++{0x41CF, 0x9E}, ++{0x41D0, 0xA3}, ++{0x41D1, 0xA3}, ++{0x41D2, 0xA2}, ++{0x41D3, 0xA1}, ++{0x41D4, 0xA7}, ++{0x41D5, 0xA7}, ++{0x41D6, 0xA7}, ++{0x41D7, 0xA3}, ++{0x4200, 0xCE}, ++{0x4201, 0xC0}, ++{0x4202, 0xC1}, ++{0x4203, 0xB9}, ++{0x4204, 0xC3}, ++{0x4205, 0xB9}, ++{0x4206, 0xBC}, ++{0x4207, 0xBD}, ++{0x4208, 0xB3}, ++{0x4209, 0xAE}, ++{0x420A, 0xAF}, ++{0x420B, 0xAE}, ++{0x420C, 0xAA}, ++{0x420D, 0xA8}, ++{0x420E, 0xA8}, ++{0x420F, 0xA6}, ++{0x4210, 0xA4}, ++{0x4211, 0xA2}, ++{0x4212, 0xA2}, ++{0x4213, 0xA0}, ++{0x4214, 0xA4}, ++{0x4215, 0xA3}, ++{0x4216, 0xA2}, ++{0x4217, 0xA0}, ++{0x4218, 0xA7}, ++{0x4219, 0xA5}, ++{0x421A, 0xA3}, ++{0x421B, 0xA1}, ++{0x421C, 0xB0}, ++{0x421D, 0xA8}, ++{0x421E, 0xA8}, ++{0x421F, 0xA6}, ++{0x4220, 0xB4}, ++{0x4221, 0xAA}, ++{0x4222, 0xA5}, ++{0x4223, 0xA3}, ++{0x4224, 0xC7}, ++{0x4225, 0xBC}, ++{0x4226, 0xBE}, ++{0x4227, 0xBC}, ++{0x4228, 0xB0}, ++{0x4229, 0xA9}, ++{0x422A, 0xA9}, ++{0x422B, 0xA8}, ++{0x422C, 0xA0}, ++{0x422D, 0x9D}, ++{0x422E, 0x9D}, ++{0x422F, 0x9C}, ++{0x4230, 0x94}, ++{0x4231, 0x93}, ++{0x4232, 0x93}, ++{0x4233, 0x92}, ++{0x4234, 0x8E}, ++{0x4235, 0x8D}, ++{0x4236, 0x8D}, ++{0x4237, 0x8C}, ++{0x4238, 0x8F}, ++{0x4239, 0x8E}, ++{0x423A, 0x8E}, ++{0x423B, 0x8D}, ++{0x423C, 0x96}, ++{0x423D, 0x94}, ++{0x423E, 0x94}, ++{0x423F, 0x92}, ++{0x4240, 0xA1}, ++{0x4241, 0x9C}, ++{0x4242, 0x9C}, ++{0x4243, 0x99}, ++{0x4244, 0xB0}, ++{0x4245, 0xA8}, ++{0x4246, 0xAB}, ++{0x4247, 0xA7}, ++{0x4248, 0xC3}, ++{0x4249, 0xB7}, ++{0x424A, 0xB7}, ++{0x424B, 0xBC}, ++{0x424C, 0xAB}, ++{0x424D, 0xA4}, ++{0x424E, 0xA5}, ++{0x424F, 0xA5}, ++{0x4250, 0x9A}, ++{0x4251, 0x97}, ++{0x4252, 0x97}, ++{0x4253, 0x98}, ++{0x4254, 0x8C}, ++{0x4255, 0x8B}, ++{0x4256, 0x8B}, ++{0x4257, 0x8B}, ++{0x4258, 0x82}, ++{0x4259, 0x82}, ++{0x425A, 0x82}, ++{0x425B, 0x82}, ++{0x425C, 0x85}, ++{0x425D, 0x85}, ++{0x425E, 0x85}, ++{0x425F, 0x84}, ++{0x4260, 0x8F}, ++{0x4261, 0x8E}, ++{0x4262, 0x8E}, ++{0x4263, 0x8D}, ++{0x4264, 0x9B}, ++{0x4265, 0x98}, ++{0x4266, 0x98}, ++{0x4267, 0x95}, ++{0x4268, 0xAE}, ++{0x4269, 0xA5}, ++{0x426A, 0xA7}, ++{0x426B, 0xA2}, ++{0x426C, 0xC2}, ++{0x426D, 0xB7}, ++{0x426E, 0xB8}, ++{0x426F, 0xB9}, ++{0x4270, 0xAA}, ++{0x4271, 0xA4}, ++{0x4272, 0xA4}, ++{0x4273, 0xA5}, ++{0x4274, 0x99}, ++{0x4275, 0x96}, ++{0x4276, 0x97}, ++{0x4277, 0x98}, ++{0x4278, 0x8B}, ++{0x4279, 0x8A}, ++{0x427A, 0x8A}, ++{0x427B, 0x8B}, ++{0x427C, 0x81}, ++{0x427D, 0x81}, ++{0x427E, 0x81}, ++{0x427F, 0x82}, ++{0x4280, 0x84}, ++{0x4281, 0x84}, ++{0x4282, 0x84}, ++{0x4283, 0x84}, ++{0x4284, 0x8E}, ++{0x4285, 0x8E}, ++{0x4286, 0x8D}, ++{0x4287, 0x8C}, ++{0x4288, 0x9A}, ++{0x4289, 0x97}, ++{0x428A, 0x97}, ++{0x428B, 0x95}, ++{0x428C, 0xAA}, ++{0x428D, 0xA3}, ++{0x428E, 0xA3}, ++{0x428F, 0xA2}, ++{0x4290, 0xC7}, ++{0x4291, 0xBA}, ++{0x4292, 0xC0}, ++{0x4293, 0xC3}, ++{0x4294, 0xB0}, ++{0x4295, 0xA7}, ++{0x4296, 0xA7}, ++{0x4297, 0xA9}, ++{0x4298, 0x9F}, ++{0x4299, 0x9B}, ++{0x429A, 0x9B}, ++{0x429B, 0x9D}, ++{0x429C, 0x93}, ++{0x429D, 0x91}, ++{0x429E, 0x91}, ++{0x429F, 0x92}, ++{0x42A0, 0x8C}, ++{0x42A1, 0x8B}, ++{0x42A2, 0x8B}, ++{0x42A3, 0x8C}, ++{0x42A4, 0x8D}, ++{0x42A5, 0x8C}, ++{0x42A6, 0x8C}, ++{0x42A7, 0x8C}, ++{0x42A8, 0x94}, ++{0x42A9, 0x93}, ++{0x42AA, 0x92}, ++{0x42AB, 0x91}, ++{0x42AC, 0x9E}, ++{0x42AD, 0x9B}, ++{0x42AE, 0x9B}, ++{0x42AF, 0x98}, ++{0x42B0, 0xAC}, ++{0x42B1, 0xA6}, ++{0x42B2, 0xA6}, ++{0x42B3, 0xA2}, ++{0x42B4, 0xCE}, ++{0x42B5, 0xBA}, ++{0x42B6, 0xBC}, ++{0x42B7, 0xB7}, ++{0x42B8, 0xC5}, ++{0x42B9, 0xB5}, ++{0x42BA, 0xBA}, ++{0x42BB, 0xC0}, ++{0x42BC, 0xB1}, ++{0x42BD, 0xA8}, ++{0x42BE, 0xAE}, ++{0x42BF, 0xAF}, ++{0x42C0, 0xA7}, ++{0x42C1, 0xA3}, ++{0x42C2, 0xA3}, ++{0x42C3, 0xA5}, ++{0x42C4, 0xA0}, ++{0x42C5, 0x9D}, ++{0x42C6, 0x9D}, ++{0x42C7, 0x9F}, ++{0x42C8, 0xA0}, ++{0x42C9, 0x9E}, ++{0x42CA, 0x9E}, ++{0x42CB, 0x9F}, ++{0x42CC, 0xA2}, ++{0x42CD, 0xA0}, ++{0x42CE, 0xA0}, ++{0x42CF, 0xA0}, ++{0x42D0, 0xA8}, ++{0x42D1, 0xA5}, ++{0x42D2, 0xA5}, ++{0x42D3, 0xA2}, ++{0x42D4, 0xB3}, ++{0x42D5, 0xAA}, ++{0x42D6, 0xAB}, ++{0x42D7, 0xA3}, ++{0x42D8, 0x00}, ++{0x42D9, 0x00}, ++{0x4300, 0xA2}, ++{0x4301, 0xAE}, ++{0x4302, 0xAD}, ++{0x4303, 0xB5}, ++{0x4304, 0x95}, ++{0x4305, 0x9A}, ++{0x4306, 0x98}, ++{0x4307, 0x9B}, ++{0x4308, 0x8D}, ++{0x4309, 0x90}, ++{0x430A, 0x8F}, ++{0x430B, 0x91}, ++{0x430C, 0x86}, ++{0x430D, 0x88}, ++{0x430E, 0x87}, ++{0x430F, 0x89}, ++{0x4310, 0x86}, ++{0x4311, 0x87}, ++{0x4312, 0x86}, ++{0x4313, 0x88}, ++{0x4314, 0x89}, ++{0x4315, 0x88}, ++{0x4316, 0x88}, ++{0x4317, 0x8E}, ++{0x4318, 0x90}, ++{0x4319, 0x8F}, ++{0x431A, 0x8C}, ++{0x431B, 0x8C}, ++{0x431C, 0x9C}, ++{0x431D, 0x99}, ++{0x431E, 0x98}, ++{0x431F, 0x99}, ++{0x4320, 0xAB}, ++{0x4321, 0xB0}, ++{0x4322, 0xAD}, ++{0x4323, 0xAF}, ++{0x4324, 0x9B}, ++{0x4325, 0x9F}, ++{0x4326, 0x9E}, ++{0x4327, 0xA1}, ++{0x4328, 0x8E}, ++{0x4329, 0x91}, ++{0x432A, 0x90}, ++{0x432B, 0x93}, ++{0x432C, 0x86}, ++{0x432D, 0x88}, ++{0x432E, 0x87}, ++{0x432F, 0x89}, ++{0x4330, 0x82}, ++{0x4331, 0x84}, ++{0x4332, 0x83}, ++{0x4333, 0x84}, ++{0x4334, 0x82}, ++{0x4335, 0x82}, ++{0x4336, 0x82}, ++{0x4337, 0x83}, ++{0x4338, 0x85}, ++{0x4339, 0x84}, ++{0x433A, 0x84}, ++{0x433B, 0x85}, ++{0x433C, 0x8A}, ++{0x433D, 0x89}, ++{0x433E, 0x88}, ++{0x433F, 0x89}, ++{0x4340, 0x93}, ++{0x4341, 0x91}, ++{0x4342, 0x91}, ++{0x4343, 0x93}, ++{0x4344, 0xA0}, ++{0x4345, 0x9E}, ++{0x4346, 0x9D}, ++{0x4347, 0xA1}, ++{0x4348, 0x95}, ++{0x4349, 0x9B}, ++{0x434A, 0x9A}, ++{0x434B, 0x9C}, ++{0x434C, 0x8A}, ++{0x434D, 0x8D}, ++{0x434E, 0x8C}, ++{0x434F, 0x8D}, ++{0x4350, 0x83}, ++{0x4351, 0x85}, ++{0x4352, 0x84}, ++{0x4353, 0x85}, ++{0x4354, 0x80}, ++{0x4355, 0x81}, ++{0x4356, 0x81}, ++{0x4357, 0x81}, ++{0x4358, 0x80}, ++{0x4359, 0x80}, ++{0x435A, 0x80}, ++{0x435B, 0x80}, ++{0x435C, 0x82}, ++{0x435D, 0x81}, ++{0x435E, 0x81}, ++{0x435F, 0x81}, ++{0x4360, 0x85}, ++{0x4361, 0x84}, ++{0x4362, 0x84}, ++{0x4363, 0x85}, ++{0x4364, 0x8D}, ++{0x4365, 0x8B}, ++{0x4366, 0x8B}, ++{0x4367, 0x8D}, ++{0x4368, 0x98}, ++{0x4369, 0x98}, ++{0x436A, 0x95}, ++{0x436B, 0x98}, ++{0x436C, 0x95}, ++{0x436D, 0x9A}, ++{0x436E, 0x99}, ++{0x436F, 0x9A}, ++{0x4370, 0x8A}, ++{0x4371, 0x8D}, ++{0x4372, 0x8C}, ++{0x4373, 0x8C}, ++{0x4374, 0x83}, ++{0x4375, 0x85}, ++{0x4376, 0x84}, ++{0x4377, 0x84}, ++{0x4378, 0x80}, ++{0x4379, 0x80}, ++{0x437A, 0x80}, ++{0x437B, 0x80}, ++{0x437C, 0x7F}, ++{0x437D, 0x7F}, ++{0x437E, 0x7F}, ++{0x437F, 0x7F}, ++{0x4380, 0x81}, ++{0x4381, 0x80}, ++{0x4382, 0x80}, ++{0x4383, 0x81}, ++{0x4384, 0x84}, ++{0x4385, 0x83}, ++{0x4386, 0x83}, ++{0x4387, 0x84}, ++{0x4388, 0x8B}, ++{0x4389, 0x8A}, ++{0x438A, 0x8A}, ++{0x438B, 0x8C}, ++{0x438C, 0x97}, ++{0x438D, 0x96}, ++{0x438E, 0x96}, ++{0x438F, 0x99}, ++{0x4390, 0x99}, ++{0x4391, 0x9F}, ++{0x4392, 0x9E}, ++{0x4393, 0x9D}, ++{0x4394, 0x8D}, ++{0x4395, 0x90}, ++{0x4396, 0x90}, ++{0x4397, 0x8F}, ++{0x4398, 0x85}, ++{0x4399, 0x87}, ++{0x439A, 0x87}, ++{0x439B, 0x86}, ++{0x439C, 0x81}, ++{0x439D, 0x83}, ++{0x439E, 0x82}, ++{0x439F, 0x82}, ++{0x43A0, 0x80}, ++{0x43A1, 0x81}, ++{0x43A2, 0x81}, ++{0x43A3, 0x81}, ++{0x43A4, 0x82}, ++{0x43A5, 0x82}, ++{0x43A6, 0x82}, ++{0x43A7, 0x82}, ++{0x43A8, 0x86}, ++{0x43A9, 0x85}, ++{0x43AA, 0x85}, ++{0x43AB, 0x87}, ++{0x43AC, 0x8D}, ++{0x43AD, 0x8D}, ++{0x43AE, 0x8D}, ++{0x43AF, 0x90}, ++{0x43B0, 0x9A}, ++{0x43B1, 0x9A}, ++{0x43B2, 0x9B}, ++{0x43B3, 0x9D}, ++{0x43B4, 0xA0}, ++{0x43B5, 0xAD}, ++{0x43B6, 0xAC}, ++{0x43B7, 0xAA}, ++{0x43B8, 0x93}, ++{0x43B9, 0x97}, ++{0x43BA, 0x97}, ++{0x43BB, 0x96}, ++{0x43BC, 0x8B}, ++{0x43BD, 0x8E}, ++{0x43BE, 0x8E}, ++{0x43BF, 0x8C}, ++{0x43C0, 0x83}, ++{0x43C1, 0x85}, ++{0x43C2, 0x85}, ++{0x43C3, 0x84}, ++{0x43C4, 0x82}, ++{0x43C5, 0x84}, ++{0x43C6, 0x83}, ++{0x43C7, 0x83}, ++{0x43C8, 0x83}, ++{0x43C9, 0x84}, ++{0x43CA, 0x84}, ++{0x43CB, 0x85}, ++{0x43CC, 0x8A}, ++{0x43CD, 0x8A}, ++{0x43CE, 0x8A}, ++{0x43CF, 0x8C}, ++{0x43D0, 0x92}, ++{0x43D1, 0x93}, ++{0x43D2, 0x93}, ++{0x43D3, 0x96}, ++{0x43D4, 0x9F}, ++{0x43D5, 0xA6}, ++{0x43D6, 0xA5}, ++{0x43D7, 0xAA}, ++{0x4400, 0xA1}, ++{0x4401, 0xAB}, ++{0x4402, 0xA7}, ++{0x4403, 0xB0}, ++{0x4404, 0x91}, ++{0x4405, 0x96}, ++{0x4406, 0x94}, ++{0x4407, 0x99}, ++{0x4408, 0x8A}, ++{0x4409, 0x8E}, ++{0x440A, 0x8C}, ++{0x440B, 0x8F}, ++{0x440C, 0x85}, ++{0x440D, 0x86}, ++{0x440E, 0x86}, ++{0x440F, 0x88}, ++{0x4410, 0x85}, ++{0x4411, 0x86}, ++{0x4412, 0x85}, ++{0x4413, 0x87}, ++{0x4414, 0x88}, ++{0x4415, 0x87}, ++{0x4416, 0x87}, ++{0x4417, 0x89}, ++{0x4418, 0x91}, ++{0x4419, 0x8F}, ++{0x441A, 0x8F}, ++{0x441B, 0x90}, ++{0x441C, 0x9C}, ++{0x441D, 0x9B}, ++{0x441E, 0x9A}, ++{0x441F, 0x9A}, ++{0x4420, 0xB3}, ++{0x4421, 0xB1}, ++{0x4422, 0xB0}, ++{0x4423, 0xB2}, ++{0x4424, 0x96}, ++{0x4425, 0x9C}, ++{0x4426, 0x9A}, ++{0x4427, 0x9E}, ++{0x4428, 0x8B}, ++{0x4429, 0x8F}, ++{0x442A, 0x8E}, ++{0x442B, 0x91}, ++{0x442C, 0x84}, ++{0x442D, 0x87}, ++{0x442E, 0x86}, ++{0x442F, 0x88}, ++{0x4430, 0x82}, ++{0x4431, 0x83}, ++{0x4432, 0x82}, ++{0x4433, 0x84}, ++{0x4434, 0x82}, ++{0x4435, 0x82}, ++{0x4436, 0x82}, ++{0x4437, 0x83}, ++{0x4438, 0x84}, ++{0x4439, 0x84}, ++{0x443A, 0x84}, ++{0x443B, 0x84}, ++{0x443C, 0x8B}, ++{0x443D, 0x89}, ++{0x443E, 0x89}, ++{0x443F, 0x89}, ++{0x4440, 0x95}, ++{0x4441, 0x93}, ++{0x4442, 0x93}, ++{0x4443, 0x93}, ++{0x4444, 0xA2}, ++{0x4445, 0xA2}, ++{0x4446, 0xA1}, ++{0x4447, 0xA0}, ++{0x4448, 0x8F}, ++{0x4449, 0x97}, ++{0x444A, 0x97}, ++{0x444B, 0x98}, ++{0x444C, 0x87}, ++{0x444D, 0x8B}, ++{0x444E, 0x8A}, ++{0x444F, 0x8B}, ++{0x4450, 0x81}, ++{0x4451, 0x83}, ++{0x4452, 0x83}, ++{0x4453, 0x84}, ++{0x4454, 0x7F}, ++{0x4455, 0x80}, ++{0x4456, 0x80}, ++{0x4457, 0x81}, ++{0x4458, 0x80}, ++{0x4459, 0x80}, ++{0x445A, 0x80}, ++{0x445B, 0x80}, ++{0x445C, 0x82}, ++{0x445D, 0x81}, ++{0x445E, 0x81}, ++{0x445F, 0x81}, ++{0x4460, 0x87}, ++{0x4461, 0x85}, ++{0x4462, 0x85}, ++{0x4463, 0x86}, ++{0x4464, 0x90}, ++{0x4465, 0x8E}, ++{0x4466, 0x8E}, ++{0x4467, 0x8E}, ++{0x4468, 0x9B}, ++{0x4469, 0x9C}, ++{0x446A, 0x9A}, ++{0x446B, 0x9A}, ++{0x446C, 0x91}, ++{0x446D, 0x97}, ++{0x446E, 0x95}, ++{0x446F, 0x95}, ++{0x4470, 0x87}, ++{0x4471, 0x8A}, ++{0x4472, 0x8A}, ++{0x4473, 0x89}, ++{0x4474, 0x81}, ++{0x4475, 0x83}, ++{0x4476, 0x83}, ++{0x4477, 0x83}, ++{0x4478, 0x7F}, ++{0x4479, 0x80}, ++{0x447A, 0x80}, ++{0x447B, 0x80}, ++{0x447C, 0x80}, ++{0x447D, 0x80}, ++{0x447E, 0x80}, ++{0x447F, 0x7F}, ++{0x4480, 0x81}, ++{0x4481, 0x81}, ++{0x4482, 0x81}, ++{0x4483, 0x81}, ++{0x4484, 0x85}, ++{0x4485, 0x85}, ++{0x4486, 0x85}, ++{0x4487, 0x85}, ++{0x4488, 0x8E}, ++{0x4489, 0x8D}, ++{0x448A, 0x8D}, ++{0x448B, 0x8E}, ++{0x448C, 0x9D}, ++{0x448D, 0x9C}, ++{0x448E, 0x9C}, ++{0x448F, 0x9C}, ++{0x4490, 0x94}, ++{0x4491, 0x9B}, ++{0x4492, 0x9A}, ++{0x4493, 0x97}, ++{0x4494, 0x8A}, ++{0x4495, 0x8E}, ++{0x4496, 0x8E}, ++{0x4497, 0x8C}, ++{0x4498, 0x84}, ++{0x4499, 0x86}, ++{0x449A, 0x86}, ++{0x449B, 0x84}, ++{0x449C, 0x81}, ++{0x449D, 0x83}, ++{0x449E, 0x83}, ++{0x449F, 0x81}, ++{0x44A0, 0x81}, ++{0x44A1, 0x82}, ++{0x44A2, 0x82}, ++{0x44A3, 0x81}, ++{0x44A4, 0x83}, ++{0x44A5, 0x83}, ++{0x44A6, 0x83}, ++{0x44A7, 0x83}, ++{0x44A8, 0x88}, ++{0x44A9, 0x88}, ++{0x44AA, 0x88}, ++{0x44AB, 0x88}, ++{0x44AC, 0x91}, ++{0x44AD, 0x91}, ++{0x44AE, 0x91}, ++{0x44AF, 0x92}, ++{0x44B0, 0xA0}, ++{0x44B1, 0xA0}, ++{0x44B2, 0xA0}, ++{0x44B3, 0xA0}, ++{0x44B4, 0x9E}, ++{0x44B5, 0xA9}, ++{0x44B6, 0xA8}, ++{0x44B7, 0xA3}, ++{0x44B8, 0x90}, ++{0x44B9, 0x95}, ++{0x44BA, 0x95}, ++{0x44BB, 0x92}, ++{0x44BC, 0x8A}, ++{0x44BD, 0x8E}, ++{0x44BE, 0x8E}, ++{0x44BF, 0x8B}, ++{0x44C0, 0x84}, ++{0x44C1, 0x86}, ++{0x44C2, 0x86}, ++{0x44C3, 0x84}, ++{0x44C4, 0x84}, ++{0x44C5, 0x85}, ++{0x44C6, 0x85}, ++{0x44C7, 0x84}, ++{0x44C8, 0x86}, ++{0x44C9, 0x87}, ++{0x44CA, 0x87}, ++{0x44CB, 0x86}, ++{0x44CC, 0x8D}, ++{0x44CD, 0x8E}, ++{0x44CE, 0x8E}, ++{0x44CF, 0x8D}, ++{0x44D0, 0x98}, ++{0x44D1, 0x98}, ++{0x44D2, 0x99}, ++{0x44D3, 0x9A}, ++{0x44D4, 0xA9}, ++{0x44D5, 0xAA}, ++{0x44D6, 0xAA}, ++{0x44D7, 0xAD}, ++{0x4500, 0x9F}, ++{0x4501, 0xA8}, ++{0x4502, 0xA5}, ++{0x4503, 0xAF}, ++{0x4504, 0x8F}, ++{0x4505, 0x96}, ++{0x4506, 0x92}, ++{0x4507, 0x94}, ++{0x4508, 0x89}, ++{0x4509, 0x8D}, ++{0x450A, 0x8A}, ++{0x450B, 0x8E}, ++{0x450C, 0x84}, ++{0x450D, 0x85}, ++{0x450E, 0x84}, ++{0x450F, 0x87}, ++{0x4510, 0x84}, ++{0x4511, 0x85}, ++{0x4512, 0x84}, ++{0x4513, 0x86}, ++{0x4514, 0x87}, ++{0x4515, 0x86}, ++{0x4516, 0x86}, ++{0x4517, 0x88}, ++{0x4518, 0x8F}, ++{0x4519, 0x8D}, ++{0x451A, 0x8D}, ++{0x451B, 0x8F}, ++{0x451C, 0x9A}, ++{0x451D, 0x9A}, ++{0x451E, 0x98}, ++{0x451F, 0x9A}, ++{0x4520, 0xAF}, ++{0x4521, 0xAF}, ++{0x4522, 0xB2}, ++{0x4523, 0xB1}, ++{0x4524, 0x95}, ++{0x4525, 0x9B}, ++{0x4526, 0x97}, ++{0x4527, 0x9C}, ++{0x4528, 0x8A}, ++{0x4529, 0x8E}, ++{0x452A, 0x8D}, ++{0x452B, 0x90}, ++{0x452C, 0x84}, ++{0x452D, 0x86}, ++{0x452E, 0x85}, ++{0x452F, 0x87}, ++{0x4530, 0x81}, ++{0x4531, 0x82}, ++{0x4532, 0x82}, ++{0x4533, 0x83}, ++{0x4534, 0x81}, ++{0x4535, 0x81}, ++{0x4536, 0x81}, ++{0x4537, 0x82}, ++{0x4538, 0x84}, ++{0x4539, 0x83}, ++{0x453A, 0x83}, ++{0x453B, 0x84}, ++{0x453C, 0x8A}, ++{0x453D, 0x88}, ++{0x453E, 0x88}, ++{0x453F, 0x89}, ++{0x4540, 0x94}, ++{0x4541, 0x92}, ++{0x4542, 0x91}, ++{0x4543, 0x92}, ++{0x4544, 0xA1}, ++{0x4545, 0xA0}, ++{0x4546, 0x9C}, ++{0x4547, 0x9D}, ++{0x4548, 0x8F}, ++{0x4549, 0x96}, ++{0x454A, 0x95}, ++{0x454B, 0x92}, ++{0x454C, 0x87}, ++{0x454D, 0x8A}, ++{0x454E, 0x89}, ++{0x454F, 0x8A}, ++{0x4550, 0x81}, ++{0x4551, 0x83}, ++{0x4552, 0x82}, ++{0x4553, 0x83}, ++{0x4554, 0x7F}, ++{0x4555, 0x80}, ++{0x4556, 0x80}, ++{0x4557, 0x81}, ++{0x4558, 0x7F}, ++{0x4559, 0x80}, ++{0x455A, 0x7F}, ++{0x455B, 0x80}, ++{0x455C, 0x81}, ++{0x455D, 0x81}, ++{0x455E, 0x81}, ++{0x455F, 0x81}, ++{0x4560, 0x86}, ++{0x4561, 0x85}, ++{0x4562, 0x85}, ++{0x4563, 0x85}, ++{0x4564, 0x8F}, ++{0x4565, 0x8D}, ++{0x4566, 0x8D}, ++{0x4567, 0x8D}, ++{0x4568, 0x99}, ++{0x4569, 0x9A}, ++{0x456A, 0x97}, ++{0x456B, 0x99}, ++{0x456C, 0x90}, ++{0x456D, 0x95}, ++{0x456E, 0x93}, ++{0x456F, 0x92}, ++{0x4570, 0x87}, ++{0x4571, 0x8A}, ++{0x4572, 0x88}, ++{0x4573, 0x87}, ++{0x4574, 0x81}, ++{0x4575, 0x83}, ++{0x4576, 0x82}, ++{0x4577, 0x82}, ++{0x4578, 0x7F}, ++{0x4579, 0x80}, ++{0x457A, 0x80}, ++{0x457B, 0x80}, ++{0x457C, 0x80}, ++{0x457D, 0x80}, ++{0x457E, 0x80}, ++{0x457F, 0x80}, ++{0x4580, 0x81}, ++{0x4581, 0x81}, ++{0x4582, 0x81}, ++{0x4583, 0x81}, ++{0x4584, 0x85}, ++{0x4585, 0x85}, ++{0x4586, 0x84}, ++{0x4587, 0x85}, ++{0x4588, 0x8E}, ++{0x4589, 0x8D}, ++{0x458A, 0x8C}, ++{0x458B, 0x8D}, ++{0x458C, 0x9B}, ++{0x458D, 0x9B}, ++{0x458E, 0x9A}, ++{0x458F, 0x98}, ++{0x4590, 0x94}, ++{0x4591, 0x9A}, ++{0x4592, 0x94}, ++{0x4593, 0x90}, ++{0x4594, 0x8A}, ++{0x4595, 0x8D}, ++{0x4596, 0x8C}, ++{0x4597, 0x89}, ++{0x4598, 0x84}, ++{0x4599, 0x86}, ++{0x459A, 0x85}, ++{0x459B, 0x83}, ++{0x459C, 0x82}, ++{0x459D, 0x83}, ++{0x459E, 0x82}, ++{0x459F, 0x80}, ++{0x45A0, 0x81}, ++{0x45A1, 0x82}, ++{0x45A2, 0x81}, ++{0x45A3, 0x80}, ++{0x45A4, 0x83}, ++{0x45A5, 0x83}, ++{0x45A6, 0x83}, ++{0x45A7, 0x83}, ++{0x45A8, 0x88}, ++{0x45A9, 0x87}, ++{0x45AA, 0x87}, ++{0x45AB, 0x88}, ++{0x45AC, 0x91}, ++{0x45AD, 0x90}, ++{0x45AE, 0x90}, ++{0x45AF, 0x91}, ++{0x45B0, 0x9F}, ++{0x45B1, 0x9F}, ++{0x45B2, 0x9E}, ++{0x45B3, 0x9F}, ++{0x45B4, 0x9F}, ++{0x45B5, 0xA8}, ++{0x45B6, 0xA6}, ++{0x45B7, 0xA7}, ++{0x45B8, 0x8D}, ++{0x45B9, 0x95}, ++{0x45BA, 0x90}, ++{0x45BB, 0x8A}, ++{0x45BC, 0x89}, ++{0x45BD, 0x8D}, ++{0x45BE, 0x88}, ++{0x45BF, 0x86}, ++{0x45C0, 0x84}, ++{0x45C1, 0x86}, ++{0x45C2, 0x85}, ++{0x45C3, 0x82}, ++{0x45C4, 0x84}, ++{0x45C5, 0x85}, ++{0x45C6, 0x85}, ++{0x45C7, 0x83}, ++{0x45C8, 0x86}, ++{0x45C9, 0x86}, ++{0x45CA, 0x86}, ++{0x45CB, 0x85}, ++{0x45CC, 0x8E}, ++{0x45CD, 0x8D}, ++{0x45CE, 0x8D}, ++{0x45CF, 0x8C}, ++{0x45D0, 0x99}, ++{0x45D1, 0x98}, ++{0x45D2, 0x98}, ++{0x45D3, 0x98}, ++{0x45D4, 0xA6}, ++{0x45D5, 0xA9}, ++{0x45D6, 0xA7}, ++{0x45D7, 0xAC}, ++{0x7000, 0xAB}, ++{0x7001, 0xBA}, ++{0x7002, 0x40}, ++{0x7003, 0x02}, ++{0x7004, 0x00}, ++{0x7005, 0x00}, ++{0x7006, 0x00}, ++{0x7007, 0x00}, ++{0x7008, 0x00}, ++{0x7009, 0x00}, ++{0x700A, 0x00}, ++{0x700B, 0x00}, ++{0x700C, 0x00}, ++{0x700D, 0x00}, ++{0x700E, 0x00}, ++{0x700F, 0x00}, ++{0x7010, 0x55}, ++{0x7011, 0x88}, ++{0x7012, 0x40}, ++{0x7013, 0x01}, ++{0x7014, 0x72}, ++{0x7015, 0xF1}, ++{0x7016, 0x02}, ++{0x7017, 0xF8}, ++{0x7018, 0x00}, ++{0x7019, 0x00}, ++{0x701A, 0x00}, ++{0x701B, 0x00}, ++{0x701C, 0x00}, ++{0x701D, 0x00}, ++{0x701E, 0x00}, ++{0x701F, 0x00}, ++{0x7020, 0x00}, ++{0x7021, 0x00}, ++{0x7022, 0x00}, ++{0x7023, 0x00}, ++{0x7024, 0x00}, ++{0x7025, 0x00}, ++{0x7026, 0x00}, ++{0x7027, 0x00}, ++{0x7028, 0x00}, ++{0x7029, 0x00}, ++{0x702A, 0x00}, ++{0x702B, 0x00}, ++{0x702C, 0x00}, ++{0x702D, 0x00}, ++{0x702E, 0x00}, ++{0x702F, 0x00}, ++{0x7030, 0x00}, ++{0x7031, 0x00}, ++{0x7032, 0x00}, ++{0x7033, 0x00}, ++{0x7034, 0x00}, ++{0x7035, 0x00}, ++{0x7036, 0x00}, ++{0x7037, 0x00}, ++{0x7038, 0x00}, ++{0x7039, 0x00}, ++{0x703A, 0x00}, ++{0x703B, 0x00}, ++{0x703C, 0x00}, ++{0x703D, 0x00}, ++{0x703E, 0x00}, ++{0x703F, 0x00}, ++{0x7040, 0x00}, ++{0x7041, 0x00}, ++{0x7042, 0x00}, ++{0x7043, 0x00}, ++{0x7044, 0x00}, ++{0x7045, 0x00}, ++{0x7046, 0x00}, ++{0x7047, 0x00}, ++{0x7048, 0x00}, ++{0x7049, 0x00}, ++{0x704A, 0x00}, ++{0x704B, 0x00}, ++{0x704C, 0x00}, ++{0x704D, 0x00}, ++{0x704E, 0x00}, ++{0x704F, 0x00}, ++{0x7050, 0x00}, ++{0x7051, 0x00}, ++{0x7052, 0x00}, ++{0x7053, 0x00}, ++{0x7054, 0x00}, ++{0x7055, 0x00}, ++{0x7056, 0x00}, ++{0x7057, 0x00}, ++{0x7058, 0x00}, ++{0x7059, 0x00}, ++{0x705A, 0x00}, ++{0x705B, 0x00}, ++{0x705C, 0x00}, ++{0x705D, 0x00}, ++{0x705E, 0x00}, ++{0x705F, 0x00}, ++{0x7060, 0x00}, ++{0x7061, 0x00}, ++{0x7062, 0x00}, ++{0x7063, 0x00}, ++{0x7064, 0x00}, ++{0x7065, 0x00}, ++{0x7066, 0x00}, ++{0x7067, 0x00}, ++{0x7068, 0x00}, ++{0x7069, 0x00}, ++{0x706A, 0x00}, ++{0x706B, 0x00}, ++{0x706C, 0x00}, ++{0x706D, 0x00}, ++{0x706E, 0x00}, ++{0x706F, 0x00}, ++{0x7070, 0x00}, ++{0x7071, 0x00}, ++{0x7072, 0x00}, ++{0x7073, 0x00}, ++{0x7074, 0x00}, ++{0x7075, 0x00}, ++{0x7076, 0x00}, ++{0x7077, 0x00}, ++{0x7078, 0x00}, ++{0x7079, 0x00}, ++{0x707A, 0x00}, ++{0x707B, 0x00}, ++{0x707C, 0x00}, ++{0x707D, 0x00}, ++{0x707E, 0x00}, ++{0x707F, 0x00}, ++{0x7080, 0x00}, ++{0x7081, 0x00}, ++{0x7082, 0x00}, ++{0x7083, 0x00}, ++{0x7084, 0x00}, ++{0x7085, 0x00}, ++{0x7086, 0x00}, ++{0x7087, 0x00}, ++{0x7088, 0x00}, ++{0x7089, 0x00}, ++{0x708A, 0x00}, ++{0x708B, 0x00}, ++{0x708C, 0x00}, ++{0x708D, 0x00}, ++{0x708E, 0x00}, ++{0x708F, 0x00}, ++{0x7090, 0x00}, ++{0x7091, 0xF0}, ++{0x7092, 0x02}, ++{0x7093, 0xF8}, ++{0x7094, 0x8D}, ++{0x7095, 0xF6}, ++{0x7096, 0xFA}, ++{0x7097, 0xFF}, ++{0x7098, 0xF0}, ++{0x7099, 0xB5}, ++{0x709A, 0x04}, ++{0x709B, 0x46}, ++{0x709C, 0x8F}, ++{0x709D, 0xB0}, ++{0x709E, 0x5F}, ++{0x709F, 0x48}, ++{0x70A0, 0x0C}, ++{0x70A1, 0x90}, ++{0x70A2, 0x5F}, ++{0x70A3, 0x48}, ++{0x70A4, 0x06}, ++{0x70A5, 0x90}, ++{0x70A6, 0x20}, ++{0x70A7, 0x46}, ++{0x70A8, 0x34}, ++{0x70A9, 0x30}, ++{0x70AA, 0x0B}, ++{0x70AB, 0x90}, ++{0x70AC, 0x5B}, ++{0x70AD, 0x48}, ++{0x70AE, 0x5A}, ++{0x70AF, 0x49}, ++{0x70B0, 0x26}, ++{0x70B1, 0x46}, ++{0x70B2, 0x66}, ++{0x70B3, 0x30}, ++{0x70B4, 0x3A}, ++{0x70B5, 0x31}, ++{0x70B6, 0x3C}, ++{0x70B7, 0x36}, ++{0x70B8, 0x05}, ++{0x70B9, 0x90}, ++{0x70BA, 0x0A}, ++{0x70BB, 0x30}, ++{0x70BC, 0x04}, ++{0x70BD, 0x90}, ++{0x70BE, 0x59}, ++{0x70BF, 0x48}, ++{0x70C0, 0x55}, ++{0x70C1, 0x4A}, ++{0x70C2, 0x40}, ++{0x70C3, 0x6E}, ++{0x70C4, 0xC0}, ++{0x70C5, 0x07}, ++{0x70C6, 0x7D}, ++{0x70C7, 0xD1}, ++{0x70C8, 0x17}, ++{0x70C9, 0x88}, ++{0x70CA, 0x0A}, ++{0x70CB, 0x5E}, ++{0x70CC, 0x0D}, ++{0x70CD, 0x92}, ++{0x70CE, 0x53}, ++{0x70CF, 0x49}, ++{0x70D0, 0x55}, ++{0x70D1, 0x48}, ++{0x70D2, 0x94}, ++{0x70D3, 0x31}, ++{0x70D4, 0x89}, ++{0x70D5, 0x6B}, ++{0x70D6, 0x80}, ++{0x70D7, 0x68}, ++{0x70D8, 0x09}, ++{0x70D9, 0x02}, ++{0x70DA, 0x00}, ++{0x70DB, 0x03}, ++{0x70DC, 0x09}, ++{0x70DD, 0x0E}, ++{0x70DE, 0x00}, ++{0x70DF, 0x0B}, ++{0x70E0, 0x49}, ++{0x70E1, 0x1C}, ++{0x70E2, 0x48}, ++{0x70E3, 0x43}, ++{0x70E4, 0x4D}, ++{0x70E5, 0x49}, ++{0x70E6, 0x6C}, ++{0x70E7, 0x39}, ++{0x70E8, 0x8A}, ++{0x70E9, 0x6A}, ++{0x70EA, 0x07}, ++{0x70EB, 0x92}, ++{0x70EC, 0xCA}, ++{0x70ED, 0x6A}, ++{0x70EE, 0x00}, ++{0x70EF, 0x21}, ++{0x70F0, 0xC9}, ++{0x70F1, 0x43}, ++{0x70F2, 0x03}, ++{0x70F3, 0x92}, ++{0x70F4, 0x00}, ++{0x70F5, 0x22}, ++{0x70F6, 0x00}, ++{0x70F7, 0x91}, ++{0x70F8, 0x01}, ++{0x70F9, 0x92}, ++{0x70FA, 0x39}, ++{0x70FB, 0x46}, ++{0x70FC, 0x8F}, ++{0x70FD, 0xF6}, ++{0x70FE, 0xCE}, ++{0x70FF, 0xFB}, ++{0x7100, 0x01}, ++{0x7101, 0x22}, ++{0x7102, 0x00}, ++{0x7103, 0x23}, ++{0x7104, 0x8C}, ++{0x7105, 0xF6}, ++{0x7106, 0x02}, ++{0x7107, 0xFA}, ++{0x7108, 0x00}, ++{0x7109, 0x21}, ++{0x710A, 0x05}, ++{0x710B, 0x46}, ++{0x710C, 0x01}, ++{0x710D, 0x91}, ++{0x710E, 0x00}, ++{0x710F, 0x90}, ++{0x7110, 0x39}, ++{0x7111, 0x46}, ++{0x7112, 0x07}, ++{0x7113, 0x98}, ++{0x7114, 0x8F}, ++{0x7115, 0xF6}, ++{0x7116, 0xC2}, ++{0x7117, 0xFB}, ++{0x7118, 0x0D}, ++{0x7119, 0x9A}, ++{0x711A, 0xD3}, ++{0x711B, 0x17}, ++{0x711C, 0x80}, ++{0x711D, 0x18}, ++{0x711E, 0x59}, ++{0x711F, 0x41}, ++{0x7120, 0x01}, ++{0x7121, 0x22}, ++{0x7122, 0x00}, ++{0x7123, 0x23}, ++{0x7124, 0x8C}, ++{0x7125, 0xF6}, ++{0x7126, 0xCD}, ++{0x7127, 0xF9}, ++{0x7128, 0x07}, ++{0x7129, 0x90}, ++{0x712A, 0x00}, ++{0x712B, 0x20}, ++{0x712C, 0x01}, ++{0x712D, 0x90}, ++{0x712E, 0x00}, ++{0x712F, 0x95}, ++{0x7130, 0x39}, ++{0x7131, 0x46}, ++{0x7132, 0x03}, ++{0x7133, 0x98}, ++{0x7134, 0x8F}, ++{0x7135, 0xF6}, ++{0x7136, 0xB2}, ++{0x7137, 0xFB}, ++{0x7138, 0x01}, ++{0x7139, 0x22}, ++{0x713A, 0x00}, ++{0x713B, 0x23}, ++{0x713C, 0x8C}, ++{0x713D, 0xF6}, ++{0x713E, 0xE6}, ++{0x713F, 0xF9}, ++{0x7140, 0x02}, ++{0x7141, 0x46}, ++{0x7142, 0x07}, ++{0x7143, 0x98}, ++{0x7144, 0x00}, ++{0x7145, 0x23}, ++{0x7146, 0x81}, ++{0x7147, 0x0B}, ++{0x7148, 0x80}, ++{0x7149, 0x04}, ++{0x714A, 0x7A}, ++{0x714B, 0xF6}, ++{0x714C, 0x54}, ++{0x714D, 0xF8}, ++{0x714E, 0x37}, ++{0x714F, 0x4A}, ++{0x7150, 0x00}, ++{0x7151, 0x23}, ++{0x7152, 0x00}, ++{0x7153, 0x92}, ++{0x7154, 0x01}, ++{0x7155, 0x93}, ++{0x7156, 0x01}, ++{0x7157, 0x22}, ++{0x7158, 0x8C}, ++{0x7159, 0xF6}, ++{0x715A, 0xD8}, ++{0x715B, 0xF9}, ++{0x715C, 0x05}, ++{0x715D, 0x46}, ++{0x715E, 0x60}, ++{0x715F, 0x68}, ++{0x7160, 0x00}, ++{0x7161, 0x23}, ++{0x7162, 0x01}, ++{0x7163, 0x0C}, ++{0x7164, 0x00}, ++{0x7165, 0x04}, ++{0x7166, 0xE2}, ++{0x7167, 0x68}, ++{0x7168, 0x7A}, ++{0x7169, 0xF6}, ++{0x716A, 0x45}, ++{0x716B, 0xF8}, ++{0x716C, 0x00}, ++{0x716D, 0x22}, ++{0x716E, 0xD2}, ++{0x716F, 0x43}, ++{0x7170, 0x00}, ++{0x7171, 0x23}, ++{0x7172, 0x00}, ++{0x7173, 0x92}, ++{0x7174, 0x01}, ++{0x7175, 0x93}, ++{0x7176, 0x1A}, ++{0x7177, 0x46}, ++{0x7178, 0x8C}, ++{0x7179, 0xF6}, ++{0x717A, 0xC8}, ++{0x717B, 0xF9}, ++{0x717C, 0x29}, ++{0x717D, 0x46}, ++{0x717E, 0x8F}, ++{0x717F, 0xF6}, ++{0x7180, 0x8D}, ++{0x7181, 0xFB}, ++{0x7182, 0x8A}, ++{0x7183, 0x03}, ++{0x7184, 0x80}, ++{0x7185, 0x0C}, ++{0x7186, 0x10}, ++{0x7187, 0x43}, ++{0x7188, 0x00}, ++{0x7189, 0x22}, ++{0x718A, 0xD2}, ++{0x718B, 0x43}, ++{0x718C, 0x00}, ++{0x718D, 0x23}, ++{0x718E, 0x00}, ++{0x718F, 0x92}, ++{0x7190, 0x89}, ++{0x7191, 0x0C}, ++{0x7192, 0x01}, ++{0x7193, 0x93}, ++{0x7194, 0x1A}, ++{0x7195, 0x46}, ++{0x7196, 0x8C}, ++{0x7197, 0xF6}, ++{0x7198, 0xB9}, ++{0x7199, 0xF9}, ++{0x719A, 0x00}, ++{0x719B, 0x24}, ++{0x719C, 0x03}, ++{0x719D, 0x90}, ++{0x719E, 0x0C}, ++{0x719F, 0x98}, ++{0x71A0, 0x61}, ++{0x71A1, 0x00}, ++{0x71A2, 0x45}, ++{0x71A3, 0x5A}, ++{0x71A4, 0x06}, ++{0x71A5, 0x98}, ++{0x71A6, 0x22}, ++{0x71A7, 0x4A}, ++{0x71A8, 0x40}, ++{0x71A9, 0x5A}, ++{0x71AA, 0x00}, ++{0x71AB, 0x21}, ++{0x71AC, 0x8C}, ++{0x71AD, 0xF6}, ++{0x71AE, 0xBE}, ++{0x71AF, 0xF9}, ++{0x71B0, 0x07}, ++{0x71B1, 0x46}, ++{0x71B2, 0x28}, ++{0x71B3, 0x46}, ++{0x71B4, 0x03}, ++{0x71B5, 0x99}, ++{0x71B6, 0x8F}, ++{0x71B7, 0xF6}, ++{0x71B8, 0x71}, ++{0x71B9, 0xFB}, ++{0x71BA, 0x3A}, ++{0x71BB, 0x46}, ++{0x71BC, 0x00}, ++{0x71BD, 0x23}, ++{0x71BE, 0x79}, ++{0x71BF, 0xF6}, ++{0x71C0, 0xCA}, ++{0x71C1, 0xFF}, ++{0x71C2, 0x00}, ++{0x71C3, 0xE0}, ++{0x71C4, 0x0F}, ++{0x71C5, 0xE0}, ++{0x71C6, 0x8A}, ++{0x71C7, 0x02}, ++{0x71C8, 0x80}, ++{0x71C9, 0x0D}, ++{0x71CA, 0x10}, ++{0x71CB, 0x43}, ++{0x71CC, 0x19}, ++{0x71CD, 0x4A}, ++{0x71CE, 0x00}, ++{0x71CF, 0x23}, ++{0x71D0, 0x00}, ++{0x71D1, 0x92}, ++{0x71D2, 0x89}, ++{0x71D3, 0x0D}, ++{0x71D4, 0x01}, ++{0x71D5, 0x93}, ++{0x71D6, 0x40}, ++{0x71D7, 0x22}, ++{0x71D8, 0x8C}, ++{0x71D9, 0xF6}, ++{0x71DA, 0x98}, ++{0x71DB, 0xF9}, ++{0x71DC, 0xA1}, ++{0x71DD, 0x00}, ++{0x71DE, 0x64}, ++{0x71DF, 0x1C}, ++{0x71E0, 0x70}, ++{0x71E1, 0x50}, ++{0x71E2, 0x04}, ++{0x71E3, 0x2C}, ++{0x71E4, 0xDB}, ++{0x71E5, 0xD3}, ++{0x71E6, 0x14}, ++{0x71E7, 0x4D}, ++{0x71E8, 0x00}, ++{0x71E9, 0x24}, ++{0x71EA, 0x0B}, ++{0x71EB, 0x98}, ++{0x71EC, 0x67}, ++{0x71ED, 0x00}, ++{0x71EE, 0xC0}, ++{0x71EF, 0x5B}, ++{0x71F0, 0x2A}, ++{0x71F1, 0x46}, ++{0x71F2, 0x40}, ++{0x71F3, 0x21}, ++{0x71F4, 0x8C}, ++{0x71F5, 0xF6}, ++{0x71F6, 0x9A}, ++{0x71F7, 0xF9}, ++{0x71F8, 0x05}, ++{0x71F9, 0x99}, ++{0x71FA, 0x0E}, ++{0x71FB, 0x4A}, ++{0x71FC, 0xC8}, ++{0x71FD, 0x53}, ++{0x71FE, 0xA7}, ++{0x71FF, 0x00}, ++{0x7200, 0xF0}, ++{0x7201, 0x59}, ++{0x7202, 0x40}, ++{0x7203, 0x21}, ++{0x7204, 0x8C}, ++{0x7205, 0xF6}, ++{0x7206, 0x7B}, ++{0x7207, 0xF9}, ++{0x7208, 0x04}, ++{0x7209, 0x99}, ++{0x720A, 0x64}, ++{0x720B, 0x1C}, ++{0x720C, 0xC8}, ++{0x720D, 0x51}, ++{0x720E, 0x04}, ++{0x720F, 0x2C}, ++{0x7210, 0xEB}, ++{0x7211, 0xD3}, ++{0x7212, 0x0F}, ++{0x7213, 0xB0}, ++{0x7214, 0xF0}, ++{0x7215, 0xBD}, ++{0x7216, 0x00}, ++{0x7217, 0x00}, ++{0x7218, 0x76}, ++{0x7219, 0x69}, ++{0x721A, 0x18}, ++{0x721B, 0x00}, ++{0x721C, 0xEC}, ++{0x721D, 0x58}, ++{0x721E, 0x18}, ++{0x721F, 0x00}, ++{0x7220, 0x38}, ++{0x7221, 0x36}, ++{0x7222, 0x18}, ++{0x7223, 0x00}, ++{0x7224, 0x00}, ++{0x7225, 0x35}, ++{0x7226, 0x18}, ++{0x7227, 0x00}, ++{0x7228, 0x00}, ++{0x7229, 0x20}, ++{0x722A, 0x18}, ++{0x722B, 0x00}, ++{0x722C, 0xFF}, ++{0x722D, 0xFF}, ++{0x722E, 0xFF}, ++{0x722F, 0x3F}, ++{0x7230, 0xFF}, ++{0x7231, 0x07}, ++{0x7232, 0x00}, ++{0x7233, 0x00}, ++{0x7234, 0xFF}, ++{0x7235, 0xFF}, ++{0x7236, 0x07}, ++{0x7237, 0x00}, ++{0x7238, 0xFF}, ++{0x7239, 0x1F}, ++{0x723A, 0x00}, ++{0x723B, 0x00}, ++{0x723C, 0x01}, ++{0x723D, 0xF6}, ++{0x723E, 0x45}, ++{0x723F, 0x12}, ++{0x0000, 0x00}, ++}; +diff --git a/drivers/media/i2c/soc_camera/max9286.c b/drivers/media/i2c/soc_camera/max9286.c +new file mode 100644 +index 0000000..c850196 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/max9286.c +@@ -0,0 +1,692 @@ ++/* ++ * MAXIM max9286 GMSL driver ++ * ++ * Copyright (C) 2015-2018 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ + -+#define REG8_NUM_RETRIES 1 /* number of read/write retries */ -+#define REG16_NUM_RETRIES 10 /* number of read/write retries */ -+#define MAX9271_ID 0x9 -+#define MAX96705_ID 0x41 -+#define MAX9286_ID 0x40 -+#define BROADCAST 0x6f ++#include ++#include ++#include ++#include ++#include ++#include + -+static inline int reg8_read(struct i2c_client *client, u8 reg, u8 *val) -+{ -+ int ret, retries; ++#include ++#include ++#include ++#include + -+ for (retries = REG8_NUM_RETRIES; retries; retries--) { -+ ret = i2c_smbus_read_byte_data(client, reg); -+ if (!(ret < 0)) -+ break; -+ } ++#include "max9286.h" + -+ if (ret < 0) { -+ dev_dbg(&client->dev, -+ "read fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ } else { -+ *val = ret; -+ } ++#define MAXIM_I2C_I2C_SPEED_837KHZ (0x7 << 2) /* 837kbps */ ++#define MAXIM_I2C_I2C_SPEED_533KHZ (0x6 << 2) /* 533kbps */ ++#define MAXIM_I2C_I2C_SPEED_339KHZ (0x5 << 2) /* 339 kbps */ ++#define MAXIM_I2C_I2C_SPEED_173KHZ (0x4 << 2) /* 174kbps */ ++#define MAXIM_I2C_I2C_SPEED_105KHZ (0x3 << 2) /* 105 kbps */ ++#define MAXIM_I2C_I2C_SPEED_085KHZ (0x2 << 2) /* 84.7 kbps */ ++#define MAXIM_I2C_I2C_SPEED_028KHZ (0x1 << 2) /* 28.3 kbps */ ++#define MAXIM_I2C_I2C_SPEED MAXIM_I2C_I2C_SPEED_339KHZ + -+ return ret < 0 ? ret : 0; -+} ++struct max9286_priv { ++ struct v4l2_subdev sd[4]; ++ struct device_node *sd_of_node[4]; ++ int des_addr; ++ int des_quirk_addr; /* second MAX9286 on the same I2C bus */ ++ int links; ++ int links_mask; ++ int lanes; ++ int csi_rate; ++ const char *fsync_mode; ++ int fsync_period; ++ char pclk_rising_edge; ++ int gpio_resetb; ++ int active_low_resetb; ++ int him; ++ int hsync; ++ int vsync; ++ int timeout; ++ int poc_delay; ++ atomic_t use_count; ++ u32 csi2_outord; ++ struct i2c_client *client; ++ int max9271_addr_map[4]; ++ int ser_id; ++ struct gpio_desc *poc_gpio[4]; /* PoC power supply */ ++}; + -+static inline int reg8_write(struct i2c_client *client, u8 reg, u8 val) -+{ -+ int ret, retries; ++static char fsync_mode_default[20] = "manual"; /* manual, automatic, semi-automatic, external */ + -+ for (retries = REG8_NUM_RETRIES; retries; retries--) { -+ ret = i2c_smbus_write_byte_data(client, reg, val); -+ if (!(ret < 0)) -+ break; -+ } ++static int conf_link; ++module_param(conf_link, int, 0644); ++MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); + -+ if (ret < 0) { -+ dev_dbg(&client->dev, -+ "write fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ } else { -+#ifdef WRITE_VERIFY -+ u8 val2; -+ reg8_read(client, reg, &val2); -+ if (val != val2) -+ dev_err(&client->dev, -+ "write verify mismatch: chip 0x%x reg=0x%x " -+ "0x%x->0x%x\n", client->addr, reg, val, val2); -+#endif -+ } ++static int poc_trig; ++module_param(poc_trig, int, 0644); ++MODULE_PARM_DESC(poc_trig, " Use PoC triggering during reverse channel setup. Useful on systems with dedicated PoC and unstable ser-des lock"); + -+ return ret < 0 ? ret : 0; -+} ++static int him; ++module_param(him, int, 0644); ++MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)"); + -+static inline int reg16_read(struct i2c_client *client, u16 reg, u8 *val) -+{ -+ int ret, retries; -+ u8 buf[2] = {reg >> 8, reg & 0xff}; ++static int fsync_period; ++module_param(fsync_period, int, 0644); ++MODULE_PARM_DESC(fsync_period, " Frame sync period (default: 3.2MHz)"); + -+ for (retries = REG16_NUM_RETRIES; retries; retries--) { -+ ret = i2c_master_send(client, buf, 2); -+ if (ret == 2) { -+ ret = i2c_master_recv(client, buf, 1); -+ if (ret == 1) -+ break; -+ } -+ } ++static int hsync; ++module_param(hsync, int, 0644); ++MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)"); + -+ if (ret < 0) { -+ dev_dbg(&client->dev, -+ "read fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ } else { -+ *val = buf[0]; -+ } ++static int vsync = 1; ++module_param(vsync, int, 0644); ++MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)"); + -+ return ret < 0 ? ret : 0; -+} ++static int gpio_resetb; ++module_param(gpio_resetb, int, 0644); ++MODULE_PARM_DESC(gpio_resetb, " Serializer GPIO reset (default: 0 - not used)"); + -+static inline int reg16_write(struct i2c_client *client, u16 reg, u8 val) -+{ -+ int ret, retries; -+ u8 buf[3] = {reg >> 8, reg & 0xff, val}; ++static int active_low_resetb; ++module_param(active_low_resetb, int, 0644); ++MODULE_PARM_DESC(active_low_resetb, " Serializer GPIO reset level (default: 0 - active high)"); + -+ for (retries = REG16_NUM_RETRIES; retries; retries--) { -+ ret = i2c_master_send(client, buf, 3); -+ if (ret == 3) -+ break; -+ } ++static int poc_delay; ++module_param(poc_delay, int, 0644); ++MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 0 ms)"); + -+ if (ret < 0) { -+ dev_dbg(&client->dev, -+ "write fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ } else { -+#ifdef WRITE_VERIFY -+ u8 val2; -+ reg16_read(client, reg, &val2); -+ if (val != val2) -+ dev_err(&client->dev, -+ "write verify mismatch: chip 0x%x reg=0x%x " -+ "0x%x->0x%x\n", client->addr, reg, val, val2); -+#endif ++static char* ser_name(int id) ++{ ++ switch (id) { ++ case MAX9271_ID: ++ return "MAX9271"; ++ case MAX96705_ID: ++ return "MAX96705"; ++ default: ++ return "unknown"; + } -+ -+ return ret < 0 ? ret : 0; +} + -+ -+static inline int reg16_read16(struct i2c_client *client, u16 reg, u16 *val) ++static void max9286_preinit(struct i2c_client *client, int addr) +{ -+ int ret, retries; -+ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ struct max9286_priv *priv = i2c_get_clientdata(client); + -+ for (retries = REG8_NUM_RETRIES; retries; retries--) { -+ ret = i2c_master_send(client, buf, 2); -+ if (ret == 2) { -+ ret = i2c_master_recv(client, buf, 2); -+ if (ret == 2) -+ break; -+ } -+ } ++ client->addr = addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x0a, 0x00); /* disable reverse control for all cams */ ++ reg8_write(client, 0x00, 0x00); /* disable all GMSL links [0:3] */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ reg8_write(client, 0x1c, priv->him ? 0xf4 : 0x04); /* high-immunity or legacy mode */ ++} + -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "read fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ } else { -+ *val = ((u16)buf[0] << 8) | buf[1]; -+ } ++static void max9286_sensor_reset(struct i2c_client *client, int addr, int reset_on) ++{ ++ struct max9286_priv *priv = i2c_get_clientdata(client); + -+ return ret < 0 ? ret : 0; ++ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) ++ return; ++ ++ /* sensor reset/unreset */ ++ client->addr = addr; /* MAX9271-CAMx I2C */ ++ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | /* set GPIOn value to reset/unreset */ ++ ((priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0) ^ reset_on)); ++ reg8_write(client, 0x0e, 0x42 | BIT(priv->gpio_resetb)); /* set GPIOn direction output */ +} + -+static inline int reg16_write16(struct i2c_client *client, u16 reg, u16 val) ++static void max9286_postinit(struct i2c_client *client, int addr) +{ -+ int ret, retries; -+ u8 buf[4] = {reg >> 8, reg & 0xff, val >> 8, val & 0xff}; ++ struct max9286_priv *priv = i2c_get_clientdata(client); ++ int idx; + -+ for (retries = REG8_NUM_RETRIES; retries; retries--) { -+ ret = i2c_master_send(client, buf, 4); -+ if (ret == 4) -+ break; -+ } ++ for (idx = 0; idx < priv->links; idx++) { ++ client->addr = priv->des_addr; /* MAX9286 I2C */ ++ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */ + -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "write fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); ++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */ ++ max9286_sensor_reset(client, client->addr, 0); /* sensor unreset */ + } + -+ return ret < 0 ? ret : 0; -+} ++ client->addr = addr; /* MAX9286 I2C */ ++ reg8_write(client, 0x0a, 0x00); /* disable reverse control for all cams */ ++ reg8_write(client, 0x00, 0xe0 | priv->links_mask); /* enable GMSL link for CAMs */ ++ reg8_write(client, 0x0b, priv->csi2_outord); /* CSI2 output order */ ++ reg8_write(client, 0x15, 0x9b); /* enable CSI output, VC is set accordingly to Link number, BIT7 magic must be set */ ++ reg8_write(client, 0x1b, priv->links_mask); /* enable equalizer for CAMs */ ++ usleep_range(5000, 5500); /* wait 2ms after any change of reverse channel settings */ + ++ if (strcmp(priv->fsync_mode, "manual") == 0) { ++ reg8_write(client, 0x01, 0x00); /* manual: FRAMESYNC set manually via [0x06:0x08] regs */ ++ } else if (strcmp(priv->fsync_mode, "automatic") == 0) { ++ reg8_write(client, 0x01, 0x02); /* automatic: FRAMESYNC taken from the slowest Link */ ++ } else if (strcmp(priv->fsync_mode, "semi-automatic") == 0) { ++ reg8_write(client, 0x01, 0x01); /* semi-automatic: FRAMESYNC taken from the slowest Link */ ++ } else if (strcmp(priv->fsync_mode, "external") == 0) { ++ reg8_write(client, 0x01, 0xc0); /* ECU (aka MCU) based FrameSync using GPI-to-GPO */ ++ } ++} + -+#ifdef MAXIM_DUMP -+static void maxim_ovsensor_dump_regs(struct i2c_client *client) ++static int max9286_reverse_channel_setup(struct i2c_client *client, int idx) +{ -+ int ret, i; ++ struct max9286_priv *priv = i2c_get_clientdata(client); + u8 val = 0; -+ u16 regs[] = {0x300a, 0x300b, 0x300c}; -+ -+ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); ++ int timeout = priv->timeout; ++ char timeout_str[10]; ++ int ret = 0; + -+ for (i = 0; i < sizeof(regs) / 2; i++) { -+ ret = reg16_read(client, regs[i], &val); -+ if (ret < 0) -+ dev_err(&client->dev, -+ "read fail: chip 0x%x register 0x%02x: %d\n", -+ client->addr, regs[i], ret); -+ printk("0x%02x -> 0x%x\n", regs[i], val); -+ } -+} ++ /* Reverse channel enable */ ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x3f, 0x4f); /* enable custom reverse channel & first pulse length */ ++ reg8_write(client, 0x34, 0xa2 | MAXIM_I2C_I2C_SPEED); /* enable artificial ACKs, I2C speed set */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ reg8_write(client, 0x00, 0xe0 | BIT(idx)); /* enable GMSL link for CAMx */ ++ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse control for CAMx */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ + -+static void maxim_ov10635_dump_format_regs(struct i2c_client *client) -+{ -+ int ret, i; -+ u8 val; -+ u16 regs[] = {0x3003, 0x3004, 0x4300, -+ 0x4605, 0x3621, 0x3702, 0x3703, 0x3704, -+ 0x3802, 0x3803, 0x3806, 0x3807, 0x3808, 0x3809, 0x380a, -+ 0x380b, 0x380c, 0x380d, 0x380e, 0x380f, -+ 0x4606, 0x4607, 0x460a, 0x460b, -+ 0xc488, 0xc489, 0xc48a, 0xc48b, -+ 0xc4cc, 0xc4cd, 0xc4ce, 0xc4cf, 0xc512, 0xc513, -+ 0xc518, 0xc519, 0xc51a, 0xc51b, -+ }; ++ for (;;) { ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x3b, 0x1e); /* first pulse length rise time changed from 300ns to 200ns, amplitude 100mV */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ + -+ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); ++ client->addr = 0x40; /* MAX9271-CAMx I2C */ ++ reg8_write(client, 0x04, 0x43); /* wake-up, enable reverse_control/conf_link */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ reg8_write(client, 0x08, 0x01); /* reverse channel receiver high threshold enable */ ++ reg8_write(client, 0x97, priv->him ? 0xaf : 0x5f); /* enable reverse control channel programming (MAX96705-MAX96711 only) */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ + -+ for (i = 0; i < sizeof(regs) / 2; i++) { -+ ret = reg16_read(client, regs[i], &val); -+ if (ret < 0) -+ dev_err(&client->dev, -+ "read fail: chip 0x%x register 0x%02x: %d\n", -+ client->addr, regs[i], ret); -+ printk("0x%02x -> 0x%x\n", regs[i], val); -+ } -+} ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x3b, 0x19); /* reverse channel increase amplitude 170mV to compensate high threshold enabled */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ + -+static void maxim_max927x_dump_regs(struct i2c_client *client) -+{ -+ int ret; -+ u8 reg; ++ client->addr = 0x40; /* MAX9271-CAMx I2C */ ++ reg8_read(client, 0x1e, &val); /* read max9271 ID */ ++ if (val == MAX9271_ID || val == MAX96705_ID || --timeout == 0) { ++ priv->ser_id = val; ++ break; ++ } + -+ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); ++ /* Check if already initialized (after reboot/reset ?) */ ++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C */ ++ reg8_read(client, 0x1e, &val); /* read max9271 ID */ ++ if (val == MAX9271_ID || val == MAX96705_ID) { ++ priv->ser_id = val; ++ reg8_write(client, 0x04, 0x43); /* enable reverse_control/conf_link */ ++ usleep_range(2000, 2500); /* wait 2ms after any change of reverse channel settings */ ++ ret = -EADDRINUSE; ++ break; ++ } + -+ for (reg = 0; reg < 0x20; reg++) { -+ ret = i2c_smbus_read_byte_data(client, reg); -+ if (ret < 0) -+ dev_err(&client->dev, -+ "read fail: chip 0x%x register 0x%x: %d\n", -+ client->addr, reg, ret); -+ printk("0x%02x ", ret); -+ if (((reg + 1) % 0x10) == 0) -+ printk("\n"); ++ if (timeout == priv->timeout / 2 && poc_trig) { ++ if (!IS_ERR(priv->poc_gpio[idx])) { ++ gpiod_direction_output(priv->poc_gpio[idx], 0); /* POC power off */ ++ mdelay(200); ++ gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */ ++ mdelay(priv->poc_delay); ++ } ++ } + } -+} -+#endif /* MAXIM_DUMP */ -+#endif /* _MAX9286_MAX9271_H */ -diff --git a/drivers/media/i2c/soc_camera/ov10635.c b/drivers/media/i2c/soc_camera/ov10635.c -new file mode 100644 -index 0000000..8c06e59 ---- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov10635.c -@@ -0,0 +1,759 @@ -+/* -+ * OmniVision ov10635 sensor camera driver -+ * -+ * Copyright (C) 2015-2017 Cogent Embedded, Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ */ + -+#include -+#include -+#include -+#include -+#include ++ max9286_sensor_reset(client, client->addr, 1); /* sensor reset */ + -+#include -+#include -+#include -+#include ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } + -+#include "max9286.h" -+#include "ov10635.h" ++ priv->links_mask |= BIT(idx); ++ priv->csi2_outord &= ~(0x3 << (idx * 2)); ++ priv->csi2_outord |= ((hweight8(priv->links_mask) - 1) << (idx * 2)); + -+#define OV10635_I2C_ADDR 0x30 ++out: ++ sprintf(timeout_str, "retries=%d", priv->timeout - timeout); ++ dev_info(&client->dev, "link%d %s %sat 0x%x %s %s\n", idx, ser_name(priv->ser_id), ++ ret == -EADDRINUSE ? "already " : "", priv->max9271_addr_map[idx], ++ ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "", ++ priv->timeout - timeout? timeout_str : ""); + -+#define OV10635_PID 0x300a -+#define OV10635_VER 0x300b -+#define OV10635_VERSION_REG 0xa635 -+#define OV10635_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) ++ return ret; ++} + -+struct ov10635_priv { -+ struct v4l2_subdev sd; -+ struct v4l2_ctrl_handler hdl; -+ struct media_pad pad; -+ struct v4l2_rect rect; -+ int subsampling; -+ int fps_denominator; -+ int init_complete; -+ u8 id[6]; -+ int dvp_order; -+ /* serializers */ -+ int max9286_addr; -+ int max9271_addr; -+ int ti964_addr; -+ int ti954_addr; -+ int ti9x3_addr; -+ int port; -+ int gpio_resetb; -+ int gpio_fsin; -+}; -+ -+static inline struct ov10635_priv *to_ov10635(const struct i2c_client *client) ++static void max9286_initial_setup(struct i2c_client *client) +{ -+ return container_of(i2c_get_clientdata(client), struct ov10635_priv, sd); -+} ++ struct max9286_priv *priv = i2c_get_clientdata(client); + -+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -+{ -+ return &container_of(ctrl->handler, struct ov10635_priv, hdl)->sd; -+} ++ /* Initial setup */ ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x15, 0x13); /* disable CSI output, VC is set accordingly to Link number */ ++ reg8_write(client, 0x69, 0x0f); /* mask CSI forwarding from all links */ ++ switch (priv->lanes) { ++ case 1: ++ reg8_write(client, 0x12, 0x33); /* enable CSI-2 Lane D0, DBL mode, YUV422 8-bit*/ ++ break; ++ case 2: ++ reg8_write(client, 0x12, 0x73); /* enable CSI-2 Lanes D0,D1, DBL mode, YUV422 8-bit*/ ++ break; ++ case 3: ++ reg8_write(client, 0x12, 0xd3); /* enable CSI-2 Lanes D0-D2, DBL mode, YUV422 8-bit*/ ++ break; ++ case 4: ++ reg8_write(client, 0x12, 0xf3); /* enable CSI-2 Lanes D0-D3, DBL mode, YUV422 8-bit*/ ++ break; ++ default: ++ dev_err(&client->dev, "CSI2 lanes number is invalid (%d)\n", priv->lanes); ++ } + -+static void ov10635_s_port(struct i2c_client *client, int fwd_en) -+{ -+ struct ov10635_priv *priv = to_ov10635(client); -+ int tmp_addr; ++ /* Start GMSL initialization with FSYNC disabled. This is required for some odd LVDS cameras */ ++ reg8_write(client, 0x01, 0xc0); /* ECU (aka MCU) based FrameSync using GPI-to-GPO */ ++ reg8_write(client, 0x06, priv->fsync_period & 0xff); ++ reg8_write(client, 0x07, (priv->fsync_period >> 8) & 0xff); ++ reg8_write(client, 0x08, priv->fsync_period >> 16); + -+ if (priv->max9286_addr) { -+ tmp_addr = client->addr; -+ client->addr = priv->max9286_addr; /* Deserializer I2C address */ -+ reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */ -+ usleep_range(5000, 5500); /* wait 5ms */ -+ client->addr = tmp_addr; -+ }; ++ reg8_write(client, 0x63, 0); /* disable overlap window */ ++ reg8_write(client, 0x64, 0); ++ reg8_write(client, 0x0c, 0x91 | (priv->vsync ? BIT(3) : 0) | (priv->hsync ? BIT(2) : 0)); /* enable HS/VS encoding, use D14/15 for HS/VS, invert HS/VS */ ++ reg8_write(client, 0x19, 0x0c); /* Drive HSTRAIL state for 120ns after the last payload bit */ +} + -+static int ov10635_set_regs(struct i2c_client *client, -+ const struct ov10635_reg *regs, int nr_regs) ++static void max9286_gmsl_link_setup(struct i2c_client *client, int idx) +{ -+ int i; ++ struct max9286_priv *priv = i2c_get_clientdata(client); + -+ for (i = 0; i < nr_regs; i++) { -+ if (reg16_write(client, regs[i].reg, regs[i].val)) { -+ usleep_range(100, 150); /* wait 100ns */ -+ reg16_write(client, regs[i].reg, regs[i].val); -+ } ++ /* GMSL setup */ ++ client->addr = 0x40; /* MAX9271-CAMx I2C */ ++ reg8_write(client, 0x0d, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ ++ reg8_write(client, 0x07, 0x84 | (priv->pclk_rising_edge ? 0 : 0x10)); /* RAW/YUV, PCLK edge, HS/VS encoding enabled */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x02, 0xff); /* spread spectrum +-4%, pclk range automatic, Gbps automatic */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ ++ if (priv->ser_id == MAX96705_ID) { ++ /* setup crossbar in DBL mode: reverse DVP bus */ ++ reg8_write(client, 0x20, 0x07); ++ reg8_write(client, 0x21, 0x06); ++ reg8_write(client, 0x22, 0x05); ++ reg8_write(client, 0x23, 0x04); ++ reg8_write(client, 0x24, 0x03); ++ reg8_write(client, 0x25, 0x02); ++ reg8_write(client, 0x26, 0x01); ++ reg8_write(client, 0x27, 0x00); ++ ++ reg8_write(client, 0x30, 0x17); ++ reg8_write(client, 0x31, 0x16); ++ reg8_write(client, 0x32, 0x15); ++ reg8_write(client, 0x33, 0x14); ++ reg8_write(client, 0x34, 0x13); ++ reg8_write(client, 0x35, 0x12); ++ reg8_write(client, 0x36, 0x11); ++ reg8_write(client, 0x37, 0x10); + } + -+ return 0; -+} ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ reg8_write(client, 0x34, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ ++ usleep_range(2000, 2500); /* wait 2ms */ + -+static int ov10635_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ return 0; ++ /* I2C translator setup */ ++ client->addr = 0x40; /* MAX9271-CAMx I2C */ ++// reg8_write(client, 0x09, maxim_map[2][idx] << 1); /* SENSOR I2C translated - must be set by sensor driver */ ++// reg8_write(client, 0x0A, 0x30 << 1); /* SENSOR I2C native - must be set by sensor driver */ ++ reg8_write(client, 0x0B, BROADCAST << 1); /* broadcast I2C */ ++ reg8_write(client, 0x0C, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAMx I2C new */ ++ /* I2C addresse change */ ++ reg8_write(client, 0x01, priv->des_addr << 1); /* MAX9286 I2C */ ++ reg8_write(client, 0x00, priv->max9271_addr_map[idx] << 1); /* MAX9271-CAM0 I2C new */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ /* put MAX9271 in configuration link state */ ++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C new */ ++ reg8_write(client, 0x04, 0x43); /* enable reverse_control/conf_link */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++#ifdef MAXIM_DUMP ++ client->addr = priv->des_addr; /* MAX9286-CAMx I2C */ ++ maxim_max927x_dump_regs(client); ++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx I2C new */ ++ maxim_max927x_dump_regs(client); ++#endif +} + -+static int ov10635_set_window(struct v4l2_subdev *sd, int subsampling) ++static int max9286_initialize(struct i2c_client *client) +{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ -+ /* disable clocks */ -+ reg16_write(client, 0x302e, 0x00); -+ reg16_write(client, 0x301b, 0xff); -+ reg16_write(client, 0x301c, 0xff); -+ reg16_write(client, 0x301a, 0xff); ++ struct max9286_priv *priv = i2c_get_clientdata(client); ++ int idx, ret; + -+ /* setup resolution */ -+ reg16_write(client, 0x3808, priv->rect.width >> 8); -+ reg16_write(client, 0x3809, priv->rect.width & 0xff); -+ reg16_write(client, 0x380a, priv->rect.height >> 8); -+ reg16_write(client, 0x380b, priv->rect.height & 0xff); ++ dev_info(&client->dev, "LINKs=%d, LANES=%d, FSYNC mode=%s, FSYNC period=%d, PCLK edge=%s\n", ++ priv->links, priv->lanes, priv->fsync_mode, priv->fsync_period, ++ priv->pclk_rising_edge ? "rising" : "falling"); + -+ /* enable/disable subsampling */ -+ reg16_write(client, 0x5005, subsampling ? 0x89 : 0x08); -+ reg16_write(client, 0x3007, subsampling ? 0x02 : 0x01); -+ reg16_write(client, 0x4004, subsampling ? 0x02 : 0x04); ++ if (priv->des_quirk_addr) ++ max9286_preinit(client, priv->des_quirk_addr); + -+#if 0 /* This is implemented in VIN via SOC_CAMERA layer, hence skip */ -+ /* horiz crop start */ -+ reg16_write(client, 0x3800, priv->rect.left >> 8); -+ reg16_write(client, 0x3801, priv->rect.left & 0xff); -+ /* horiz crop end */ -+ reg16_write(client, 0x3804, (priv->rect.left + priv->rect.width + 1) >> 8); -+ reg16_write(client, 0x3805, (priv->rect.left + priv->rect.width + 1) & 0xff); -+ /* vert crop start */ -+ reg16_write(client, 0x3802, priv->rect.top >> 8); -+ reg16_write(client, 0x3803, priv->rect.top & 0xff); -+ /* vert crop end */ -+ reg16_write(client, 0x3806, (priv->rect.top + priv->rect.height + 1) >> 8); -+ reg16_write(client, 0x3807, (priv->rect.top + priv->rect.height + 1) & 0xff); -+#endif -+ /* enable clocks */ -+ reg16_write(client, 0x301b, 0xf0); -+ reg16_write(client, 0x301c, 0xf0); -+ reg16_write(client, 0x301a, 0xf0); -+ reg16_write(client, 0x302e, 0x01); ++ max9286_preinit(client, priv->des_addr); ++ max9286_initial_setup(client); + -+ return 0; -+}; ++ for (idx = 0; idx < priv->links; idx++) { ++ if (!IS_ERR(priv->poc_gpio[idx])) { ++ gpiod_direction_output(priv->poc_gpio[idx], 1); /* POC power on */ ++ mdelay(priv->poc_delay); ++ } + -+static int ov10635_get_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_format *format) -+{ -+ struct v4l2_mbus_framefmt *mf = &format->format; -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); ++ ret = max9286_reverse_channel_setup(client, idx); ++ if (ret) ++ continue; ++ max9286_gmsl_link_setup(client, idx); ++ } + -+ if (format->pad) -+ return -EINVAL; ++ max9286_postinit(client, priv->des_addr); + -+ mf->width = priv->rect.width; -+ mf->height = priv->rect.height; -+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; -+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; -+ mf->field = V4L2_FIELD_NONE; ++ client->addr = priv->des_addr; + + return 0; +} + -+static int ov10635_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_format *format) ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int max9286_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) +{ -+ struct v4l2_mbus_framefmt *mf = &format->format; ++ struct max9286_priv *priv = v4l2_get_subdevdata(sd); ++ struct i2c_client *client = priv->client; ++ int ret; ++ u8 val = 0; + -+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; -+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; -+ mf->field = V4L2_FIELD_NONE; ++ ret = reg8_read(client, (u8)reg->reg, &val); ++ if (ret < 0) ++ return ret; + -+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) -+ cfg->try_fmt = *mf; ++ reg->val = val; ++ reg->size = sizeof(u8); + + return 0; +} + -+static int ov10635_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_mbus_code_enum *code) ++static int max9286_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) +{ -+ if (code->pad || code->index > 0) -+ return -EINVAL; -+ -+ code->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ struct max9286_priv *priv = v4l2_get_subdevdata(sd); ++ struct i2c_client *client = priv->client; + -+ return 0; ++ return reg8_write(client, (u8)reg->reg, (u8)reg->val); +} ++#endif + -+static int ov10635_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) ++static int max9286_s_power(struct v4l2_subdev *sd, int on) +{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); ++ struct max9286_priv *priv = v4l2_get_subdevdata(sd); ++ struct i2c_client *client = priv->client; + -+ memcpy(edid->edid, priv->id, 6); -+ -+ edid->edid[6] = 0xff; -+ edid->edid[7] = client->addr; -+ edid->edid[8] = OV10635_VERSION_REG >> 8; -+ edid->edid[9] = OV10635_VERSION_REG & 0xff; ++ if (on) { ++ if (atomic_inc_return(&priv->use_count) == 1) ++ reg8_write(client, 0x69, priv->links_mask ^ 0x0f); /* unmask CSI forwarding from detected links */ ++ } else { ++ if (atomic_dec_return(&priv->use_count) == 0) ++ reg8_write(client, 0x69, 0x0f); /* mask CSI forwarding from all links */ ++ } + + return 0; +} + -+static int ov10635_set_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_selection *sel) ++static int max9286_registered_async(struct v4l2_subdev *sd) +{ -+ struct v4l2_rect *rect = &sel->r; -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ int subsampling = 0; -+ -+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || -+ sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; ++ struct max9286_priv *priv = v4l2_get_subdevdata(sd); ++ struct i2c_client *client = priv->client; ++ int idx, tmp_addr; + -+ rect->left = ALIGN(rect->left, 2); -+ rect->top = ALIGN(rect->top, 2); -+ rect->width = ALIGN(rect->width, 2); -+ rect->height = ALIGN(rect->height, 2); ++ /* switch to GMSL serial_link for streaming video */ ++ tmp_addr = client->addr; ++ idx = sd->grp_id; + -+ if ((rect->left + rect->width > OV10635_MAX_WIDTH) || -+ (rect->top + rect->height > OV10635_MAX_HEIGHT)) -+ *rect = priv->rect; ++ client->addr = priv->des_addr; /* MAX9286 I2C */ ++ reg8_write(client, 0x0a, 0x11 << idx); /* enable reverse/forward control for CAMx */ + -+ if (rect->width == OV10635_MAX_WIDTH / 2 && -+ rect->height == OV10635_MAX_HEIGHT / 2) -+ subsampling = 1; ++ client->addr = priv->max9271_addr_map[idx]; /* MAX9271-CAMx */ ++ reg8_write(client, 0x04, conf_link ? 0x43 : 0x83); /* enable serial_link */ ++ usleep_range(2000, 2500); /* wait 2ms after changing reverse_control */ + -+ priv->rect.left = rect->left; -+ priv->rect.top = rect->top; -+ priv->rect.width = rect->width; -+ priv->rect.height = rect->height; ++ client->addr = priv->des_addr; /* MAX9286 I2C */ ++ reg8_write(client, 0x0a, (priv->links_mask << 4) | priv->links_mask); /* enable reverse/forward control for all CAMs */ + -+ /* change window only for subsampling, crop is done by VIN */ -+ if (subsampling != priv->subsampling) { -+ ov10635_set_window(sd, subsampling); -+ priv->subsampling = subsampling; -+ } ++ client->addr = tmp_addr; + + return 0; +} + -+static int ov10635_get_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ -+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) -+ return -EINVAL; ++static struct v4l2_subdev_core_ops max9286_subdev_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = max9286_g_register, ++ .s_register = max9286_s_register, ++#endif ++ .s_power = max9286_s_power, ++ .registered_async = max9286_registered_async, ++}; + -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = OV10635_MAX_WIDTH; -+ sel->r.height = OV10635_MAX_HEIGHT; -+ return 0; -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = OV10635_MAX_WIDTH; -+ sel->r.height = OV10635_MAX_HEIGHT; -+ return 0; -+ case V4L2_SEL_TGT_CROP: -+ sel->r = priv->rect; -+ return 0; -+ default: -+ return -EINVAL; -+ } -+} ++static struct v4l2_subdev_ops max9286_subdev_ops = { ++ .core = &max9286_subdev_core_ops, ++}; + -+static int ov10635_g_mbus_config(struct v4l2_subdev *sd, -+ struct v4l2_mbus_config *cfg) ++static int max9286_parse_dt(struct i2c_client *client) +{ -+ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | -+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; -+ cfg->type = V4L2_MBUS_CSI2; ++ struct max9286_priv *priv = i2c_get_clientdata(client); ++ struct device_node *np = client->dev.of_node; ++ struct device_node *endpoint = NULL; ++ struct property *prop; ++ int err, pwen, i; ++ int sensor_delay, gpio0 = 1, gpio1 = 1; ++ u8 val = 0; ++ char poc_name[10]; + -+ return 0; -+} ++ if (of_property_read_u32(np, "maxim,links", &priv->links)) ++ priv->links = 4; + -+static int ov10635_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ struct v4l2_captureparm *cp = &parms->parm.capture; ++ if (of_property_read_u32(np, "maxim,lanes", &priv->lanes)) ++ priv->lanes = 4; + -+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; ++ pwen = of_get_gpio(np, 0); ++ if (pwen > 0) { ++ err = gpio_request_one(pwen, GPIOF_OUT_INIT_HIGH, dev_name(&client->dev)); ++ if (err) ++ dev_err(&client->dev, "cannot request PWEN gpio %d: %d\n", pwen, err); ++ } + -+ memset(cp, 0, sizeof(struct v4l2_captureparm)); -+ cp->capability = V4L2_CAP_TIMEPERFRAME; -+ cp->timeperframe.numerator = 1; -+ cp->timeperframe.denominator = priv->fps_denominator; ++ mdelay(250); + -+ return 0; -+} ++ for (i = 0; i < 4; i++) { ++ sprintf(poc_name, "POC%d", i); ++ priv->poc_gpio[i] = devm_gpiod_get_optional(&client->dev, poc_name, 0); ++ } + -+static int ov10635_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ struct v4l2_captureparm *cp = &parms->parm.capture; -+ int ret = 0; ++ reg8_read(client, 0x1e, &val); /* read max9286 ID */ ++ if (val != MAX9286_ID) { ++ prop = of_find_property(np, "reg", NULL); ++ if (prop) ++ of_remove_property(np, prop); ++ return -ENODEV; ++ } + -+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; -+ if (cp->extendedmode != 0) -+ return -EINVAL; ++ if (!of_property_read_u32(np, "maxim,gpio0", &gpio0) || ++ !of_property_read_u32(np, "maxim,gpio1", &gpio1)) ++ reg8_write(client, 0x0f, 0x08 | (gpio1 << 1) | gpio0); + -+ if (priv->fps_denominator != cp->timeperframe.denominator) { -+ switch (cp->timeperframe.denominator) { -+ case 5: -+ ret = ov10635_set_regs(client, ov10635_regs_5fps, -+ ARRAY_SIZE(ov10635_regs_5fps)); -+ break; -+ case 10: -+ ret = ov10635_set_regs(client, ov10635_regs_10fps, -+ ARRAY_SIZE(ov10635_regs_10fps)); -+ break; -+ case 15: -+ ret = ov10635_set_regs(client, ov10635_regs_15fps, -+ ARRAY_SIZE(ov10635_regs_15fps)); -+ break; -+ case 30: -+ ret = ov10635_set_regs(client, ov10635_regs_30fps, -+ ARRAY_SIZE(ov10635_regs_30fps)); -+ break; -+ default: -+ ret = -EINVAL; -+ goto out; -+ } ++ if (of_property_read_u32(np, "maxim,resetb-gpio", &priv->gpio_resetb)) { ++ priv->gpio_resetb = -1; ++ } else { ++ if (of_property_read_bool(np, "maxim,resetb-active-high")) ++ priv->active_low_resetb = 0; ++ else ++ priv->active_low_resetb = 1; ++ } + -+ priv->fps_denominator = cp->timeperframe.denominator; ++ if (!of_property_read_u32(np, "maxim,sensor_delay", &sensor_delay)) ++ mdelay(sensor_delay); ++ if (of_property_read_string(np, "maxim,fsync-mode", &priv->fsync_mode)) ++ priv->fsync_mode = fsync_mode_default; ++ if (of_property_read_u32(np, "maxim,fsync-period", &priv->fsync_period)) ++ priv->fsync_period = 3200000; /* 96MHz/30fps */ ++ priv->pclk_rising_edge = true; ++ if (of_property_read_bool(np, "maxim,pclk-falling-edge")) ++ priv->pclk_rising_edge = false; ++ if (of_property_read_u32(np, "maxim,timeout", &priv->timeout)) ++ priv->timeout = 100; ++ if (of_property_read_u32(np, "maxim,i2c-quirk", &priv->des_quirk_addr)) ++ priv->des_quirk_addr = 0; ++ if (of_property_read_u32(np, "maxim,him", &priv->him)) ++ priv->him = 0; ++ if (of_property_read_u32(np, "maxim,hsync", &priv->hsync)) ++ priv->hsync = 0; ++ if (of_property_read_u32(np, "maxim,vsync", &priv->vsync)) ++ priv->vsync = 1; ++ if (of_property_read_u32(np, "maxim,poc-delay", &priv->poc_delay)) ++ priv->poc_delay = 50; ++ ++ /* module params override dts */ ++ if (him) ++ priv->him = him; ++ if (fsync_period) { ++ priv->fsync_period = fsync_period; ++ priv->fsync_mode = fsync_mode_default; + } ++ if (hsync) ++ priv->hsync = hsync; ++ if (!vsync) ++ priv->vsync = vsync; ++ if (gpio_resetb) ++ priv->gpio_resetb = gpio_resetb; ++ if (active_low_resetb) ++ priv->active_low_resetb = active_low_resetb; ++ if (poc_delay) ++ priv->poc_delay = poc_delay; + -+out: -+ return ret; -+} ++ for (i = 0; i < priv->links; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; + -+#ifdef CONFIG_VIDEO_ADV_DEBUG -+static int ov10635_g_register(struct v4l2_subdev *sd, -+ struct v4l2_dbg_register *reg) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; -+ u8 val = 0; ++ of_node_put(endpoint); + -+ ret = reg16_read(client, (u16)reg->reg, &val); -+ if (ret < 0) -+ return ret; ++ if (of_property_read_u32(endpoint, "max9271-addr", &priv->max9271_addr_map[i])) { ++ dev_err(&client->dev, "max9271-addr not set\n"); ++ return -EINVAL; ++ } + -+ reg->val = val; -+ reg->size = sizeof(u16); ++ priv->sd_of_node[i] = endpoint; ++ } + + return 0; +} + -+static int ov10635_s_register(struct v4l2_subdev *sd, -+ const struct v4l2_dbg_register *reg) ++static void max9286_setup_remote_endpoint(struct i2c_client *client) +{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct max9286_priv *priv = i2c_get_clientdata(client); ++ struct device_node *np = client->dev.of_node; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ int i; ++ struct property *csi_rate_prop, *dvp_order_prop; + -+ return reg16_write(client, (u16)reg->reg, (u8)reg->val); -+} -+#endif ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; + -+static struct v4l2_subdev_core_ops ov10635_core_ops = { -+#ifdef CONFIG_VIDEO_ADV_DEBUG -+ .g_register = ov10635_g_register, -+ .s_register = ov10635_s_register, -+#endif -+}; ++ of_node_put(endpoint); + -+static int ov10635_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct v4l2_subdev *sd = to_sd(ctrl); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); -+ int ret = -EINVAL; -+ u8 val = 0; ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; + -+ if (!priv->init_complete) -+ return 0; ++ csi_rate_prop = of_find_property(endpoint, "csi-rate", NULL); ++ if (csi_rate_prop) { ++ /* CSI2_RATE = PCLK*sizeof(YUV8)*links/lanes */ ++ priv->csi_rate = cpu_to_be32(100 * 8 * hweight8(priv->links_mask) / priv->lanes); ++ csi_rate_prop->value = &priv->csi_rate; ++ of_update_property(rendpoint, csi_rate_prop); ++ } + -+ switch (ctrl->id) { -+ case V4L2_CID_BRIGHTNESS: -+ /* AEC/AGC target */ -+ ret = reg16_write(client, 0xc46a, ctrl->val); -+ break; -+ case V4L2_CID_CONTRAST: -+ udelay(100); -+ ret = ov10635_set_regs(client, &ov10635_regs_contrast[ctrl->val][0], 18); -+ break; -+ case V4L2_CID_SATURATION: -+ ret = reg16_write(client, 0xc316, ctrl->val); -+ break; -+ case V4L2_CID_HUE: -+ /* CMX ? */ -+ ret = 0; -+ break; -+ case V4L2_CID_GAMMA: -+ ret = reg16_write(client, 0xc4be, ctrl->val >> 8); -+ ret |= reg16_write(client, 0xc4bf, ctrl->val & 0xff); -+ break; -+ case V4L2_CID_AUTOGAIN: -+ /* automatic gain/exposure */ -+ ret = reg16_write(client, 0x56d0, !ctrl->val); -+ break; -+ case V4L2_CID_GAIN: -+ /* manual gain */ -+ ret = reg16_write(client, 0x3504, 0); -+ ret |= reg16_write(client, 0x56d1, ctrl->val >> 8); -+ ret |= reg16_write(client, 0x56d2, ctrl->val & 0xff); -+ ret |= reg16_write(client, 0x3504, 1); /* validate gain */ -+ break; -+ case V4L2_CID_EXPOSURE: -+ /* manual exposure */ -+ ret = reg16_write(client, 0x3504, 0); -+ ret |= reg16_write(client, 0x56d5, ctrl->val >> 8); -+ ret |= reg16_write(client, 0x56d6, ctrl->val & 0xff); -+ ret |= reg16_write(client, 0x3504, 1); /* validate exposure */ -+ break; -+ case V4L2_CID_HFLIP: -+ ret = reg16_read(client, 0x381d, &val); -+ if (ret < 0) -+ goto out; -+ if (ctrl->val) -+ val |= 0x3; -+ else -+ val &= ~0x3; -+ ret = reg16_write(client, 0x381d, val); -+ break; -+ case V4L2_CID_VFLIP: -+ ret = reg16_read(client, 0x381c, &val); -+ if (ctrl->val) -+ val |= 0xc0; -+ else -+ val &= ~0xc0; -+ ret = reg16_write(client, 0x381c, val); -+ break; -+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: -+ ret = 0; -+ break; ++ dvp_order_prop = of_find_property(endpoint, "dvp-order", NULL); ++ if (dvp_order_prop) ++ of_update_property(rendpoint, dvp_order_prop); + } -+ -+out: -+ return ret; +} + -+static const struct v4l2_ctrl_ops ov10635_ctrl_ops = { -+ .s_ctrl = ov10635_s_ctrl, -+}; -+ -+static struct v4l2_subdev_video_ops ov10635_video_ops = { -+ .s_stream = ov10635_s_stream, -+ .g_mbus_config = ov10635_g_mbus_config, -+ .g_parm = ov10635_g_parm, -+ .s_parm = ov10635_s_parm, -+}; -+ -+static const struct v4l2_subdev_pad_ops ov10635_subdev_pad_ops = { -+ .get_edid = ov10635_get_edid, -+ .enum_mbus_code = ov10635_enum_mbus_code, -+ .get_selection = ov10635_get_selection, -+ .set_selection = ov10635_set_selection, -+ .get_fmt = ov10635_get_fmt, -+ .set_fmt = ov10635_set_fmt, -+}; -+ -+static struct v4l2_subdev_ops ov10635_subdev_ops = { -+ .core = &ov10635_core_ops, -+ .video = &ov10635_video_ops, -+ .pad = &ov10635_subdev_pad_ops, -+}; -+ -+static void ov10635_otp_id_read(struct i2c_client *client) ++static int max9286_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) +{ -+ struct ov10635_priv *priv = to_ov10635(client); -+ int i; -+ -+ /* read camera id from OTP memory */ -+ reg16_write(client, 0x3d10, 1); -+ -+ usleep_range(15000, 16000); /* wait 15ms */ ++ struct max9286_priv *priv; ++ int err, i; + -+ for (i = 0; i < 6; i++) -+ reg16_read(client, 0x3d00 + i, &priv->id[i]); -+} ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; + -+static ssize_t ov10635_otp_id_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov10635_priv *priv = to_ov10635(client); ++ i2c_set_clientdata(client, priv); ++ priv->des_addr = client->addr; ++ priv->client = client; ++ atomic_set(&priv->use_count, 0); ++ priv->csi2_outord = 0xff; + -+ return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", -+ priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); -+} ++ err = max9286_parse_dt(client); ++ if (err) ++ goto out; + -+static DEVICE_ATTR(otp_id_ov10635, S_IRUGO, ov10635_otp_id_show, NULL); ++ err = max9286_initialize(client); ++ if (err < 0) ++ goto out; + -+static int ov10635_initialize(struct i2c_client *client) -+{ -+ struct ov10635_priv *priv = to_ov10635(client); -+ u8 pid = 0, ver = 0; -+ int ret = 0; ++ max9286_setup_remote_endpoint(client); + -+ ov10635_s_port(client, 1); ++ for (i = 0; i < 4; i++) { ++ v4l2_subdev_init(&priv->sd[i], &max9286_subdev_ops); ++ priv->sd[i].owner = client->dev.driver->owner; ++ priv->sd[i].dev = &client->dev; ++ priv->sd[i].grp_id = i; ++ v4l2_set_subdevdata(&priv->sd[i], priv); ++ priv->sd[i].of_node = priv->sd_of_node[i]; + -+ /* check and show product ID and manufacturer ID */ -+ reg16_read(client, OV10635_PID, &pid); -+ reg16_read(client, OV10635_VER, &ver); ++ snprintf(priv->sd[i].name, V4L2_SUBDEV_NAME_SIZE, "%s.%d %d-%04x", ++ client->dev.driver->name, i, i2c_adapter_id(client->adapter), ++ client->addr); + -+ if (OV10635_VERSION(pid, ver) != OV10635_VERSION_REG) { -+ dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver); -+ ret = -ENODEV; -+ goto out; ++ err = v4l2_async_register_subdev(&priv->sd[i]); ++ if (err < 0) ++ goto out; + } -+ -+ /* s/w reset sensor */ -+ reg16_write(client, 0x103, 0x1); -+ udelay(100); -+ /* Program wizard registers */ -+ ov10635_set_regs(client, ov10635_regs_wizard, ARRAY_SIZE(ov10635_regs_wizard)); -+ /* Set DVP bit swap */ -+ reg16_write(client, 0x4709, priv->dvp_order << 4); -+ /* Read OTP IDs */ -+ ov10635_otp_id_read(client); -+ -+ dev_info(&client->dev, "ov10635 Product ID %x Manufacturer ID %x OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", -+ pid, ver, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); +out: -+ ov10635_s_port(client, 0); -+ -+ return ret; ++ return err; +} + -+static int ov10635_parse_dt(struct device_node *np, struct ov10635_priv *priv) ++static int max9286_remove(struct i2c_client *client) +{ -+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ struct max9286_priv *priv = i2c_get_clientdata(client); + int i; -+ struct device_node *endpoint = NULL, *rendpoint = NULL; -+ int tmp_addr = 0; + -+ for (i = 0; ; i++) { -+ endpoint = of_graph_get_next_endpoint(np, endpoint); -+ if (!endpoint) -+ break; ++ for (i = 0; i < 4; i++) { ++ v4l2_async_unregister_subdev(&priv->sd[i]); ++ v4l2_device_unregister_subdev(&priv->sd[i]); ++ } + -+ of_node_put(endpoint); ++ return 0; ++} + -+ of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order); ++static const struct of_device_id max9286_dt_ids[] = { ++ { .compatible = "maxim,max9286" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, max9286_dt_ids); + -+ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); -+ if (!rendpoint) -+ continue; ++static const struct i2c_device_id max9286_id[] = { ++ { "max9286", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, max9286_id); + -+ if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) && -+ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) && -+ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) -+ break; ++static struct i2c_driver max9286_i2c_driver = { ++ .driver = { ++ .name = "max9286", ++ .of_match_table = of_match_ptr(max9286_dt_ids), ++ }, ++ .probe = max9286_probe, ++ .remove = max9286_remove, ++ .id_table = max9286_id, ++}; + -+ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && -+ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti964-ti9x3") && -+ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti964_addr) && -+ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) -+ break; -+ -+ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && -+ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti954-ti9x3") && -+ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti954_addr) && -+ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) -+ break; -+ } ++module_i2c_driver(max9286_i2c_driver); + -+ if (!priv->max9286_addr && !priv->ti964_addr && !priv->ti954_addr) { -+ dev_err(&client->dev, "deserializer does not present for OV10635\n"); -+ return -EINVAL; -+ } ++MODULE_DESCRIPTION("GMSL driver for MAX9286"); ++MODULE_AUTHOR("Vladimir Barinov"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/max9286.h b/drivers/media/i2c/soc_camera/max9286.h +new file mode 100644 +index 0000000..6c2a9e0 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/max9286.h +@@ -0,0 +1,244 @@ ++/* ++ * MAXIM max9286-max9271 GMSL driver include file ++ * ++ * Copyright (C) 2015-2017 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ + -+ ov10635_s_port(client, 1); ++#ifndef _MAX9286_MAX9271_H ++#define _MAX9286_MAX9271_H + -+ /* setup I2C translator address */ -+ tmp_addr = client->addr; -+ if (priv->max9286_addr) { -+ client->addr = priv->max9271_addr; /* Serializer I2C address */ ++//#define DEBUG ++#ifdef DEBUG ++//#define WRITE_VERIFY ++#define MAXIM_DUMP ++#undef dev_dbg ++#define dev_dbg dev_info ++#endif + -+ reg8_write(client, 0x09, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x0A, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ }; ++#define REG8_NUM_RETRIES 1 /* number of read/write retries */ ++#define REG16_NUM_RETRIES 10 /* number of read/write retries */ ++#define MAX9271_ID 0x9 ++#define MAX96705_ID 0x41 ++#define MAX9286_ID 0x40 ++#define BROADCAST 0x6f + -+ if (priv->ti964_addr) { -+ client->addr = priv->ti964_addr; /* Deserializer I2C address */ ++static inline int reg8_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ int ret, retries; + -+ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_read_byte_data(client, reg); ++ if (!(ret < 0)) ++ break; ++ } + -+ reg8_write(client, 0x6e, 0xa9); /* GPIO0 - resetb, GPIO1 - fsin */ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = ret; + } + -+ if (priv->ti954_addr) { -+ client->addr = priv->ti954_addr; /* Deserializer I2C address */ ++ return ret < 0 ? ret : 0; ++} + -+ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ ++static inline int reg8_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ int ret, retries; + -+ reg8_write(client, 0x6e, 0xa9); /* GPIO0 - resetb, GPIO1 - fsin */ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_write_byte_data(client, reg, val); ++ if (!(ret < 0)) ++ break; + } -+ client->addr = tmp_addr; + -+ udelay(100); ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++#ifdef WRITE_VERIFY ++ u8 val2; ++ reg8_read(client, reg, &val2); ++ if (val != val2) ++ dev_err(&client->dev, ++ "write verify mismatch: chip 0x%x reg=0x%x " ++ "0x%x->0x%x\n", client->addr, reg, val, val2); ++#endif ++ } + -+ return 0; ++ return ret < 0 ? ret : 0; +} + -+static int ov10635_probe(struct i2c_client *client, -+ const struct i2c_device_id *did) ++static inline int reg16_read(struct i2c_client *client, u16 reg, u8 *val) +{ -+ struct ov10635_priv *priv; -+ struct v4l2_ctrl *ctrl; -+ int ret; ++ int ret, retries; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; + -+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2); ++ if (ret == 2) { ++ ret = i2c_master_recv(client, buf, 1); ++ if (ret == 1) ++ break; ++ } ++ } + -+ v4l2_i2c_subdev_init(&priv->sd, client, &ov10635_subdev_ops); -+ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; -+ priv->rect.left = 0; -+ priv->rect.top = 0; -+ priv->rect.width = OV10635_MAX_WIDTH; -+ priv->rect.height = OV10635_MAX_HEIGHT; -+ priv->fps_denominator = 30; ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = buf[0]; ++ } + -+ v4l2_ctrl_handler_init(&priv->hdl, 4); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x30); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_CONTRAST, 0, 4, 1, 2); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_SATURATION, 0, 0xff, 1, 0xff); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_HUE, 0, 255, 1, 0); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_GAMMA, 0, 0xffff, 1, 0x233); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_GAIN, 0, 0x3ff, 1, 0x10); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_EXPOSURE, 0, 0xffff, 1, 0x80); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_HFLIP, 0, 1, 1, 0); -+ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_VFLIP, 0, 1, 1, 0); -+ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, -+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); -+ if (ctrl) -+ ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; -+ priv->sd.ctrl_handler = &priv->hdl; ++ return ret < 0 ? ret : 0; ++} + -+ ret = priv->hdl.error; -+ if (ret) -+ goto cleanup; ++static inline int reg16_write(struct i2c_client *client, u16 reg, u8 val) ++{ ++ int ret, retries; ++ u8 buf[3] = {reg >> 8, reg & 0xff, val}; + -+ v4l2_ctrl_handler_setup(&priv->hdl); ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 3); ++ if (ret == 3) ++ break; ++ } + -+ priv->pad.flags = MEDIA_PAD_FL_SOURCE; -+ priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; -+ ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad); -+ if (ret < 0) -+ goto cleanup; ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++#ifdef WRITE_VERIFY ++ u8 val2; ++ reg16_read(client, reg, &val2); ++ if (val != val2) ++ dev_err(&client->dev, ++ "write verify mismatch: chip 0x%x reg=0x%x " ++ "0x%x->0x%x\n", client->addr, reg, val, val2); ++#endif ++ } + -+ ret = ov10635_parse_dt(client->dev.of_node, priv); -+ if (ret) -+ goto cleanup; ++ return ret < 0 ? ret : 0; ++} + -+ ret = ov10635_initialize(client); -+ if (ret < 0) -+ goto cleanup; + -+ ret = v4l2_async_register_subdev(&priv->sd); -+ if (ret) -+ goto cleanup; ++static inline int reg16_read16(struct i2c_client *client, u16 reg, u16 *val) ++{ ++ int ret, retries; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; + -+ if (device_create_file(&client->dev, &dev_attr_otp_id_ov10635) != 0) { -+ dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); -+ goto cleanup; ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2); ++ if (ret == 2) { ++ ret = i2c_master_recv(client, buf, 2); ++ if (ret == 2) ++ break; ++ } + } + -+ priv->init_complete = 1; -+ -+ return 0; ++ if (ret < 0) { ++ dev_err(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = ((u16)buf[0] << 8) | buf[1]; ++ } + -+cleanup: -+ media_entity_cleanup(&priv->sd.entity); -+ v4l2_ctrl_handler_free(&priv->hdl); -+ v4l2_device_unregister_subdev(&priv->sd); -+#ifdef CONFIG_SOC_CAMERA_OV10635 -+ v4l_err(client, "failed to probe @ 0x%02x (%s)\n", -+ client->addr, client->adapter->name); -+#endif -+ return ret; ++ return ret < 0 ? ret : 0; +} + -+static int ov10635_remove(struct i2c_client *client) ++static inline int reg16_write16(struct i2c_client *client, u16 reg, u16 val) +{ -+ struct ov10635_priv *priv = i2c_get_clientdata(client); ++ int ret, retries; ++ u8 buf[4] = {reg >> 8, reg & 0xff, val >> 8, val & 0xff}; + -+ device_remove_file(&client->dev, &dev_attr_otp_id_ov10635); -+ v4l2_async_unregister_subdev(&priv->sd); -+ media_entity_cleanup(&priv->sd.entity); -+ v4l2_ctrl_handler_free(&priv->hdl); -+ v4l2_device_unregister_subdev(&priv->sd); ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 4); ++ if (ret == 4) ++ break; ++ } + -+ return 0; ++ if (ret < 0) { ++ dev_err(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } ++ ++ return ret < 0 ? ret : 0; +} + -+#ifdef CONFIG_SOC_CAMERA_OV10635 -+static const struct i2c_device_id ov10635_id[] = { -+ { "ov10635", 0 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, ov10635_id); + -+static const struct of_device_id ov10635_of_ids[] = { -+ { .compatible = "ovti,ov10635", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ov10635_of_ids); ++#ifdef MAXIM_DUMP ++static void maxim_ovsensor_dump_regs(struct i2c_client *client) ++{ ++ int ret, i; ++ u8 val = 0; ++ u16 regs[] = {0x300a, 0x300b, 0x300c}; + -+static struct i2c_driver ov10635_i2c_driver = { -+ .driver = { -+ .name = "ov10635", -+ .of_match_table = ov10635_of_ids, -+ }, -+ .probe = ov10635_probe, -+ .remove = ov10635_remove, -+ .id_table = ov10635_id, -+}; ++ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); + -+module_i2c_driver(ov10635_i2c_driver); ++ for (i = 0; i < sizeof(regs) / 2; i++) { ++ ret = reg16_read(client, regs[i], &val); ++ if (ret < 0) ++ dev_err(&client->dev, ++ "read fail: chip 0x%x register 0x%02x: %d\n", ++ client->addr, regs[i], ret); ++ printk("0x%02x -> 0x%x\n", regs[i], val); ++ } ++} + -+MODULE_DESCRIPTION("SoC Camera driver for OV10635"); -+MODULE_AUTHOR("Vladimir Barinov"); -+MODULE_LICENSE("GPL"); -+#endif -diff --git a/drivers/media/i2c/soc_camera/ov10635.h b/drivers/media/i2c/soc_camera/ov10635.h ++static void maxim_ov10635_dump_format_regs(struct i2c_client *client) ++{ ++ int ret, i; ++ u8 val; ++ u16 regs[] = {0x3003, 0x3004, 0x4300, ++ 0x4605, 0x3621, 0x3702, 0x3703, 0x3704, ++ 0x3802, 0x3803, 0x3806, 0x3807, 0x3808, 0x3809, 0x380a, ++ 0x380b, 0x380c, 0x380d, 0x380e, 0x380f, ++ 0x4606, 0x4607, 0x460a, 0x460b, ++ 0xc488, 0xc489, 0xc48a, 0xc48b, ++ 0xc4cc, 0xc4cd, 0xc4ce, 0xc4cf, 0xc512, 0xc513, ++ 0xc518, 0xc519, 0xc51a, 0xc51b, ++ }; ++ ++ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); ++ ++ for (i = 0; i < sizeof(regs) / 2; i++) { ++ ret = reg16_read(client, regs[i], &val); ++ if (ret < 0) ++ dev_err(&client->dev, ++ "read fail: chip 0x%x register 0x%02x: %d\n", ++ client->addr, regs[i], ret); ++ printk("0x%02x -> 0x%x\n", regs[i], val); ++ } ++} ++ ++static void maxim_max927x_dump_regs(struct i2c_client *client) ++{ ++ int ret; ++ u8 reg; ++ ++ dev_dbg(&client->dev, "dump regs 0x%x\n", client->addr); ++ ++ for (reg = 0; reg < 0x20; reg++) { ++ ret = i2c_smbus_read_byte_data(client, reg); ++ if (ret < 0) ++ dev_err(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ printk("0x%02x ", ret); ++ if (((reg + 1) % 0x10) == 0) ++ printk("\n"); ++ } ++} ++#endif /* MAXIM_DUMP */ ++#endif /* _MAX9286_MAX9271_H */ +diff --git a/drivers/media/i2c/soc_camera/ov10635.c b/drivers/media/i2c/soc_camera/ov10635.c new file mode 100644 -index 0000000..a0e510d +index 0000000..8c06e59 --- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov10635.h -@@ -0,0 +1,1139 @@ ++++ b/drivers/media/i2c/soc_camera/ov10635.c +@@ -0,0 +1,759 @@ +/* -+ * OmniVision ov10635 sensor camera wizard 1280x800@30/UYVY/BT601/8bit ++ * OmniVision ov10635 sensor camera driver + * + * Copyright (C) 2015-2017 Cogent Embedded, Inc. + * @@ -3837,35 +7451,800 @@ index 0000000..a0e510d + * option) any later version. + */ + -+//#define OV10635_DISPLAY_PATTERN ++#include ++#include ++#include ++#include ++#include + -+#define OV10635_SENSOR_WIDTH 1312 -+#define OV10635_SENSOR_HEIGHT 814 ++#include ++#include ++#include ++#include + -+#define OV10635_MAX_WIDTH 1280 -+#define OV10635_MAX_HEIGHT 800 ++#include "max9286.h" ++#include "ov10635.h" + -+//#define OV10635_PCLK_96MHZ -+#define OV10635_PCLK_88MHZ ++#define OV10635_I2C_ADDR 0x30 + -+#if defined(OV10635_PCLK_96MHZ) -+/* VTS=PCLK/FPS/HTS/2 (=96MHz/30/1600/2) */ -+ #define OV10635_HTS 1600 -+ #define OV10635_VTS 1000 /* fps=30 */ -+#elif defined(OV10635_PCLK_88MHZ) -+/* VTS=PCLK/FPS/HTS/2 (=88MHz/1572/30/2) */ -+ #define OV10635_HTS 1572 -+ #define OV10635_VTS 933 /* fps=29.9998 */ -+#else -+ #error PCLK not defined ++#define OV10635_PID 0x300a ++#define OV10635_VER 0x300b ++#define OV10635_VERSION_REG 0xa635 ++#define OV10635_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) ++ ++struct ov10635_priv { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct media_pad pad; ++ struct v4l2_rect rect; ++ int subsampling; ++ int fps_denominator; ++ int init_complete; ++ u8 id[6]; ++ int dvp_order; ++ /* serializers */ ++ int max9286_addr; ++ int max9271_addr; ++ int ti964_addr; ++ int ti954_addr; ++ int ti9x3_addr; ++ int port; ++ int gpio_resetb; ++ int gpio_fsin; ++}; ++ ++static inline struct ov10635_priv *to_ov10635(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov10635_priv, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct ov10635_priv, hdl)->sd; ++} ++ ++static void ov10635_s_port(struct i2c_client *client, int fwd_en) ++{ ++ struct ov10635_priv *priv = to_ov10635(client); ++ int tmp_addr; ++ ++ if (priv->max9286_addr) { ++ tmp_addr = client->addr; ++ client->addr = priv->max9286_addr; /* Deserializer I2C address */ ++ reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */ ++ usleep_range(5000, 5500); /* wait 5ms */ ++ client->addr = tmp_addr; ++ }; ++} ++ ++static int ov10635_set_regs(struct i2c_client *client, ++ const struct ov10635_reg *regs, int nr_regs) ++{ ++ int i; ++ ++ for (i = 0; i < nr_regs; i++) { ++ if (reg16_write(client, regs[i].reg, regs[i].val)) { ++ usleep_range(100, 150); /* wait 100ns */ ++ reg16_write(client, regs[i].reg, regs[i].val); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ov10635_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++static int ov10635_set_window(struct v4l2_subdev *sd, int subsampling) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ ++ /* disable clocks */ ++ reg16_write(client, 0x302e, 0x00); ++ reg16_write(client, 0x301b, 0xff); ++ reg16_write(client, 0x301c, 0xff); ++ reg16_write(client, 0x301a, 0xff); ++ ++ /* setup resolution */ ++ reg16_write(client, 0x3808, priv->rect.width >> 8); ++ reg16_write(client, 0x3809, priv->rect.width & 0xff); ++ reg16_write(client, 0x380a, priv->rect.height >> 8); ++ reg16_write(client, 0x380b, priv->rect.height & 0xff); ++ ++ /* enable/disable subsampling */ ++ reg16_write(client, 0x5005, subsampling ? 0x89 : 0x08); ++ reg16_write(client, 0x3007, subsampling ? 0x02 : 0x01); ++ reg16_write(client, 0x4004, subsampling ? 0x02 : 0x04); ++ ++#if 0 /* This is implemented in VIN via SOC_CAMERA layer, hence skip */ ++ /* horiz crop start */ ++ reg16_write(client, 0x3800, priv->rect.left >> 8); ++ reg16_write(client, 0x3801, priv->rect.left & 0xff); ++ /* horiz crop end */ ++ reg16_write(client, 0x3804, (priv->rect.left + priv->rect.width + 1) >> 8); ++ reg16_write(client, 0x3805, (priv->rect.left + priv->rect.width + 1) & 0xff); ++ /* vert crop start */ ++ reg16_write(client, 0x3802, priv->rect.top >> 8); ++ reg16_write(client, 0x3803, priv->rect.top & 0xff); ++ /* vert crop end */ ++ reg16_write(client, 0x3806, (priv->rect.top + priv->rect.height + 1) >> 8); ++ reg16_write(client, 0x3807, (priv->rect.top + priv->rect.height + 1) & 0xff); +#endif ++ /* enable clocks */ ++ reg16_write(client, 0x301b, 0xf0); ++ reg16_write(client, 0x301c, 0xf0); ++ reg16_write(client, 0x301a, 0xf0); ++ reg16_write(client, 0x302e, 0x01); + -+struct ov10635_reg { -+ u16 reg; -+ u8 val; ++ return 0; +}; + -+static const struct ov10635_reg ov10635_regs_wizard[] = { ++static int ov10635_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ ++ if (format->pad) ++ return -EINVAL; ++ ++ mf->width = priv->rect.width; ++ mf->height = priv->rect.height; ++ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov10635_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ ++ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) ++ cfg->try_fmt = *mf; ++ ++ return 0; ++} ++ ++static int ov10635_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->pad || code->index > 0) ++ return -EINVAL; ++ ++ code->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ ++ return 0; ++} ++ ++static int ov10635_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ ++ memcpy(edid->edid, priv->id, 6); ++ ++ edid->edid[6] = 0xff; ++ edid->edid[7] = client->addr; ++ edid->edid[8] = OV10635_VERSION_REG >> 8; ++ edid->edid[9] = OV10635_VERSION_REG & 0xff; ++ ++ return 0; ++} ++ ++static int ov10635_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct v4l2_rect *rect = &sel->r; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ int subsampling = 0; ++ ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || ++ sel->target != V4L2_SEL_TGT_CROP) ++ return -EINVAL; ++ ++ rect->left = ALIGN(rect->left, 2); ++ rect->top = ALIGN(rect->top, 2); ++ rect->width = ALIGN(rect->width, 2); ++ rect->height = ALIGN(rect->height, 2); ++ ++ if ((rect->left + rect->width > OV10635_MAX_WIDTH) || ++ (rect->top + rect->height > OV10635_MAX_HEIGHT)) ++ *rect = priv->rect; ++ ++ if (rect->width == OV10635_MAX_WIDTH / 2 && ++ rect->height == OV10635_MAX_HEIGHT / 2) ++ subsampling = 1; ++ ++ priv->rect.left = rect->left; ++ priv->rect.top = rect->top; ++ priv->rect.width = rect->width; ++ priv->rect.height = rect->height; ++ ++ /* change window only for subsampling, crop is done by VIN */ ++ if (subsampling != priv->subsampling) { ++ ov10635_set_window(sd, subsampling); ++ priv->subsampling = subsampling; ++ } ++ ++ return 0; ++} ++ ++static int ov10635_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = OV10635_MAX_WIDTH; ++ sel->r.height = OV10635_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = OV10635_MAX_WIDTH; ++ sel->r.height = OV10635_MAX_HEIGHT; ++ return 0; ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->rect; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ov10635_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ cfg->type = V4L2_MBUS_CSI2; ++ ++ return 0; ++} ++ ++static int ov10635_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(cp, 0, sizeof(struct v4l2_captureparm)); ++ cp->capability = V4L2_CAP_TIMEPERFRAME; ++ cp->timeperframe.numerator = 1; ++ cp->timeperframe.denominator = priv->fps_denominator; ++ ++ return 0; ++} ++ ++static int ov10635_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ int ret = 0; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (cp->extendedmode != 0) ++ return -EINVAL; ++ ++ if (priv->fps_denominator != cp->timeperframe.denominator) { ++ switch (cp->timeperframe.denominator) { ++ case 5: ++ ret = ov10635_set_regs(client, ov10635_regs_5fps, ++ ARRAY_SIZE(ov10635_regs_5fps)); ++ break; ++ case 10: ++ ret = ov10635_set_regs(client, ov10635_regs_10fps, ++ ARRAY_SIZE(ov10635_regs_10fps)); ++ break; ++ case 15: ++ ret = ov10635_set_regs(client, ov10635_regs_15fps, ++ ARRAY_SIZE(ov10635_regs_15fps)); ++ break; ++ case 30: ++ ret = ov10635_set_regs(client, ov10635_regs_30fps, ++ ARRAY_SIZE(ov10635_regs_30fps)); ++ break; ++ default: ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ priv->fps_denominator = cp->timeperframe.denominator; ++ } ++ ++out: ++ return ret; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov10635_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val = 0; ++ ++ ret = reg16_read(client, (u16)reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int ov10635_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return reg16_write(client, (u16)reg->reg, (u8)reg->val); ++} ++#endif ++ ++static struct v4l2_subdev_core_ops ov10635_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov10635_g_register, ++ .s_register = ov10635_s_register, ++#endif ++}; ++ ++static int ov10635_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ int ret = -EINVAL; ++ u8 val = 0; ++ ++ if (!priv->init_complete) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ /* AEC/AGC target */ ++ ret = reg16_write(client, 0xc46a, ctrl->val); ++ break; ++ case V4L2_CID_CONTRAST: ++ udelay(100); ++ ret = ov10635_set_regs(client, &ov10635_regs_contrast[ctrl->val][0], 18); ++ break; ++ case V4L2_CID_SATURATION: ++ ret = reg16_write(client, 0xc316, ctrl->val); ++ break; ++ case V4L2_CID_HUE: ++ /* CMX ? */ ++ ret = 0; ++ break; ++ case V4L2_CID_GAMMA: ++ ret = reg16_write(client, 0xc4be, ctrl->val >> 8); ++ ret |= reg16_write(client, 0xc4bf, ctrl->val & 0xff); ++ break; ++ case V4L2_CID_AUTOGAIN: ++ /* automatic gain/exposure */ ++ ret = reg16_write(client, 0x56d0, !ctrl->val); ++ break; ++ case V4L2_CID_GAIN: ++ /* manual gain */ ++ ret = reg16_write(client, 0x3504, 0); ++ ret |= reg16_write(client, 0x56d1, ctrl->val >> 8); ++ ret |= reg16_write(client, 0x56d2, ctrl->val & 0xff); ++ ret |= reg16_write(client, 0x3504, 1); /* validate gain */ ++ break; ++ case V4L2_CID_EXPOSURE: ++ /* manual exposure */ ++ ret = reg16_write(client, 0x3504, 0); ++ ret |= reg16_write(client, 0x56d5, ctrl->val >> 8); ++ ret |= reg16_write(client, 0x56d6, ctrl->val & 0xff); ++ ret |= reg16_write(client, 0x3504, 1); /* validate exposure */ ++ break; ++ case V4L2_CID_HFLIP: ++ ret = reg16_read(client, 0x381d, &val); ++ if (ret < 0) ++ goto out; ++ if (ctrl->val) ++ val |= 0x3; ++ else ++ val &= ~0x3; ++ ret = reg16_write(client, 0x381d, val); ++ break; ++ case V4L2_CID_VFLIP: ++ ret = reg16_read(client, 0x381c, &val); ++ if (ctrl->val) ++ val |= 0xc0; ++ else ++ val &= ~0xc0; ++ ret = reg16_write(client, 0x381c, val); ++ break; ++ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: ++ ret = 0; ++ break; ++ } ++ ++out: ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov10635_ctrl_ops = { ++ .s_ctrl = ov10635_s_ctrl, ++}; ++ ++static struct v4l2_subdev_video_ops ov10635_video_ops = { ++ .s_stream = ov10635_s_stream, ++ .g_mbus_config = ov10635_g_mbus_config, ++ .g_parm = ov10635_g_parm, ++ .s_parm = ov10635_s_parm, ++}; ++ ++static const struct v4l2_subdev_pad_ops ov10635_subdev_pad_ops = { ++ .get_edid = ov10635_get_edid, ++ .enum_mbus_code = ov10635_enum_mbus_code, ++ .get_selection = ov10635_get_selection, ++ .set_selection = ov10635_set_selection, ++ .get_fmt = ov10635_get_fmt, ++ .set_fmt = ov10635_set_fmt, ++}; ++ ++static struct v4l2_subdev_ops ov10635_subdev_ops = { ++ .core = &ov10635_core_ops, ++ .video = &ov10635_video_ops, ++ .pad = &ov10635_subdev_pad_ops, ++}; ++ ++static void ov10635_otp_id_read(struct i2c_client *client) ++{ ++ struct ov10635_priv *priv = to_ov10635(client); ++ int i; ++ ++ /* read camera id from OTP memory */ ++ reg16_write(client, 0x3d10, 1); ++ ++ usleep_range(15000, 16000); /* wait 15ms */ ++ ++ for (i = 0; i < 6; i++) ++ reg16_read(client, 0x3d00 + i, &priv->id[i]); ++} ++ ++static ssize_t ov10635_otp_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov10635_priv *priv = to_ov10635(client); ++ ++ return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++} ++ ++static DEVICE_ATTR(otp_id_ov10635, S_IRUGO, ov10635_otp_id_show, NULL); ++ ++static int ov10635_initialize(struct i2c_client *client) ++{ ++ struct ov10635_priv *priv = to_ov10635(client); ++ u8 pid = 0, ver = 0; ++ int ret = 0; ++ ++ ov10635_s_port(client, 1); ++ ++ /* check and show product ID and manufacturer ID */ ++ reg16_read(client, OV10635_PID, &pid); ++ reg16_read(client, OV10635_VER, &ver); ++ ++ if (OV10635_VERSION(pid, ver) != OV10635_VERSION_REG) { ++ dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ /* s/w reset sensor */ ++ reg16_write(client, 0x103, 0x1); ++ udelay(100); ++ /* Program wizard registers */ ++ ov10635_set_regs(client, ov10635_regs_wizard, ARRAY_SIZE(ov10635_regs_wizard)); ++ /* Set DVP bit swap */ ++ reg16_write(client, 0x4709, priv->dvp_order << 4); ++ /* Read OTP IDs */ ++ ov10635_otp_id_read(client); ++ ++ dev_info(&client->dev, "ov10635 Product ID %x Manufacturer ID %x OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ pid, ver, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++out: ++ ov10635_s_port(client, 0); ++ ++ return ret; ++} ++ ++static int ov10635_parse_dt(struct device_node *np, struct ov10635_priv *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ int i; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ int tmp_addr = 0; ++ ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; ++ ++ of_node_put(endpoint); ++ ++ of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order); ++ ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; ++ ++ if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ ++ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && ++ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti964-ti9x3") && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti964_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ ++ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && ++ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti954-ti9x3") && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti954_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ } ++ ++ if (!priv->max9286_addr && !priv->ti964_addr && !priv->ti954_addr) { ++ dev_err(&client->dev, "deserializer does not present for OV10635\n"); ++ return -EINVAL; ++ } ++ ++ ov10635_s_port(client, 1); ++ ++ /* setup I2C translator address */ ++ tmp_addr = client->addr; ++ if (priv->max9286_addr) { ++ client->addr = priv->max9271_addr; /* Serializer I2C address */ ++ ++ reg8_write(client, 0x09, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x0A, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ }; ++ ++ if (priv->ti964_addr) { ++ client->addr = priv->ti964_addr; /* Deserializer I2C address */ ++ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ ++ ++ reg8_write(client, 0x6e, 0xa9); /* GPIO0 - resetb, GPIO1 - fsin */ ++ } ++ ++ if (priv->ti954_addr) { ++ client->addr = priv->ti954_addr; /* Deserializer I2C address */ ++ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x5d, OV10635_I2C_ADDR << 1); /* Sensor native I2C address */ ++ ++ reg8_write(client, 0x6e, 0xa9); /* GPIO0 - resetb, GPIO1 - fsin */ ++ } ++ client->addr = tmp_addr; ++ ++ udelay(100); ++ ++ return 0; ++} ++ ++static int ov10635_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov10635_priv *priv; ++ struct v4l2_ctrl *ctrl; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&priv->sd, client, &ov10635_subdev_ops); ++ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ++ priv->rect.left = 0; ++ priv->rect.top = 0; ++ priv->rect.width = OV10635_MAX_WIDTH; ++ priv->rect.height = OV10635_MAX_HEIGHT; ++ priv->fps_denominator = 30; ++ ++ v4l2_ctrl_handler_init(&priv->hdl, 4); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x30); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 4, 1, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 0xff, 1, 0xff); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_HUE, 0, 255, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_GAMMA, 0, 0xffff, 1, 0x233); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_GAIN, 0, 0x3ff, 1, 0x10); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0xffff, 1, 0x80); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov10635_ctrl_ops, ++ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); ++ if (ctrl) ++ ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; ++ priv->sd.ctrl_handler = &priv->hdl; ++ ++ ret = priv->hdl.error; ++ if (ret) ++ goto cleanup; ++ ++ v4l2_ctrl_handler_setup(&priv->hdl); ++ ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad); ++ if (ret < 0) ++ goto cleanup; ++ ++ ret = ov10635_parse_dt(client->dev.of_node, priv); ++ if (ret) ++ goto cleanup; ++ ++ ret = ov10635_initialize(client); ++ if (ret < 0) ++ goto cleanup; ++ ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) ++ goto cleanup; ++ ++ if (device_create_file(&client->dev, &dev_attr_otp_id_ov10635) != 0) { ++ dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); ++ goto cleanup; ++ } ++ ++ priv->init_complete = 1; ++ ++ return 0; ++ ++cleanup: ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++#ifdef CONFIG_SOC_CAMERA_OV10635 ++ v4l_err(client, "failed to probe @ 0x%02x (%s)\n", ++ client->addr, client->adapter->name); ++#endif ++ return ret; ++} ++ ++static int ov10635_remove(struct i2c_client *client) ++{ ++ struct ov10635_priv *priv = i2c_get_clientdata(client); ++ ++ device_remove_file(&client->dev, &dev_attr_otp_id_ov10635); ++ v4l2_async_unregister_subdev(&priv->sd); ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SOC_CAMERA_OV10635 ++static const struct i2c_device_id ov10635_id[] = { ++ { "ov10635", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov10635_id); ++ ++static const struct of_device_id ov10635_of_ids[] = { ++ { .compatible = "ovti,ov10635", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ov10635_of_ids); ++ ++static struct i2c_driver ov10635_i2c_driver = { ++ .driver = { ++ .name = "ov10635", ++ .of_match_table = ov10635_of_ids, ++ }, ++ .probe = ov10635_probe, ++ .remove = ov10635_remove, ++ .id_table = ov10635_id, ++}; ++ ++module_i2c_driver(ov10635_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OV10635"); ++MODULE_AUTHOR("Vladimir Barinov"); ++MODULE_LICENSE("GPL"); ++#endif +diff --git a/drivers/media/i2c/soc_camera/ov10635.h b/drivers/media/i2c/soc_camera/ov10635.h +new file mode 100644 +index 0000000..a0e510d +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov10635.h +@@ -0,0 +1,1139 @@ ++/* ++ * OmniVision ov10635 sensor camera wizard 1280x800@30/UYVY/BT601/8bit ++ * ++ * Copyright (C) 2015-2017 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++//#define OV10635_DISPLAY_PATTERN ++ ++#define OV10635_SENSOR_WIDTH 1312 ++#define OV10635_SENSOR_HEIGHT 814 ++ ++#define OV10635_MAX_WIDTH 1280 ++#define OV10635_MAX_HEIGHT 800 ++ ++//#define OV10635_PCLK_96MHZ ++#define OV10635_PCLK_88MHZ ++ ++#if defined(OV10635_PCLK_96MHZ) ++/* VTS=PCLK/FPS/HTS/2 (=96MHz/30/1600/2) */ ++ #define OV10635_HTS 1600 ++ #define OV10635_VTS 1000 /* fps=30 */ ++#elif defined(OV10635_PCLK_88MHZ) ++/* VTS=PCLK/FPS/HTS/2 (=88MHz/1572/30/2) */ ++ #define OV10635_HTS 1572 ++ #define OV10635_VTS 933 /* fps=29.9998 */ ++#else ++ #error PCLK not defined ++#endif ++ ++struct ov10635_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++static const struct ov10635_reg ov10635_regs_wizard[] = { +//{0x0103, 0x01}, +{0x300C, 0x61}, +{0x300C, 0x61}, @@ -5027,10 +9406,10 @@ index 0000000..4c3515a +#endif diff --git a/drivers/media/i2c/soc_camera/ov106xx.c b/drivers/media/i2c/soc_camera/ov106xx.c new file mode 100644 -index 0000000..fa775ae +index 0000000..08786ab --- /dev/null +++ b/drivers/media/i2c/soc_camera/ov106xx.c -@@ -0,0 +1,139 @@ +@@ -0,0 +1,161 @@ +/* + * OmniVision ov10635/ov490-ov10640/ov495-ov2775 sensor camera driver + * @@ -5049,6 +9428,8 @@ index 0000000..fa775ae +#include "ar0220.c" +#include "ap0101_ar014x.c" +#include "ov2775.c" ++#include "imx390.c" ++#include "ox03a.c" + +static enum { + ID_OV10635, @@ -5058,6 +9439,8 @@ index 0000000..fa775ae + ID_AR0220, + ID_AP0101_AR014X, + ID_OV2775, ++ ID_IMX390, ++ ID_OX03A, +} chip_id; + +static int ov106xx_probe(struct i2c_client *client, @@ -5108,6 +9491,18 @@ index 0000000..fa775ae + goto out; + } + ++ ret = imx390_probe(client, did); ++ if (!ret) { ++ chip_id = ID_IMX390; ++ goto out; ++ } ++ ++ ret = ox03a_probe(client, did); ++ if (!ret) { ++ chip_id = ID_OX03A; ++ goto out; ++ } ++ + v4l_err(client, "failed to probe @ 0x%02x (%s)\n", + client->addr, client->adapter->name); +out: @@ -5138,6 +9533,12 @@ index 0000000..fa775ae + case ID_OV2775: + ov2775_remove(client); + break; ++ case ID_IMX390: ++ imx390_remove(client); ++ break; ++ case ID_OX03A: ++ ox03a_remove(client); ++ break; + }; + + return 0; @@ -5172,10 +9573,10 @@ index 0000000..fa775ae +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/ov2775.c b/drivers/media/i2c/soc_camera/ov2775.c new file mode 100644 -index 0000000..dac6d60 +index 0000000..f57fc71 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ov2775.c -@@ -0,0 +1,527 @@ +@@ -0,0 +1,538 @@ +/* + * OmniVision OV2775 sensor camera driver + * @@ -5206,7 +9607,7 @@ index 0000000..dac6d60 +#define OV2775_VER 0x300b +#define OV2775_VERSION_REG 0x2770 + -+#define OV2775_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR8_1X8 ++#define OV2775_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR12_1X12 + +struct ov2775_priv { + struct v4l2_subdev sd; @@ -5496,6 +9897,7 @@ index 0000000..dac6d60 + u8 val = 0; + u16 pid; + int ret = 0; ++ int tmp_addr; + + /* check and show model ID */ + reg16_read(client, OV2775_PID, &val); @@ -5509,6 +9911,16 @@ index 0000000..dac6d60 + goto err; + } + ++ /* setup XCLK */ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ /* CLK_OUT=22.5792*160*M/N/CLKDIV -> CLK_OUT=25MHz: CLKDIV=4, M=7, N=253: 22.5792*160/4*7/253=24.989MHz=CLK_OUT */ ++ client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++ reg8_write(client, 0x06, 0x47); /* Set CLKDIV and M */ ++ reg8_write(client, 0x07, 0xfd); /* Set N */ ++ } ++ client->addr = tmp_addr; ++ + /* Program wizard registers */ + ov2775_set_regs(client, ov2775_regs_wizard, ARRAY_SIZE(ov2775_regs_wizard)); + /* Read OTP IDs */ @@ -5705,12 +10117,12 @@ index 0000000..dac6d60 +#endif diff --git a/drivers/media/i2c/soc_camera/ov2775.h b/drivers/media/i2c/soc_camera/ov2775.h new file mode 100644 -index 0000000..1cdfb50 +index 0000000..7e1ee31 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ov2775.h @@ -0,0 +1,1841 @@ +/* -+ * OmniVision OV2775 sensor camera wizard 1928x1088@30/RGGB/MIPI ++ * OmniVision OV2775 sensor camera wizard 1920x1080@30/BGGR/MIPI + * + * Copyright (C) 2018 Cogent Embedded, Inc. + * @@ -5720,29 +10132,29 @@ index 0000000..1cdfb50 + * option) any later version. + */ + -+//#define OV2775_DISPLAY_PATTERN_COLOR_BAR ++#define OV2775_DISPLAY_PATTERN_COLOR_BAR + -+#define OV2775_MAX_WIDTH 2880 // (1928*1.5=2892) <- must be multiple of 16 - requred by R-CAR VIN -+#define OV2775_MAX_HEIGHT 1088 ++#define OV2775_MAX_WIDTH 1920 ++#define OV2775_MAX_HEIGHT 1080 + +#define OV2775_DELAY 0xffff -+#define OV2775_DT 0x2c // MIPI Data Type ++#define OV2775_DT 0x2c /* MIPI Data Type RAW12 */ + +struct ov2775_reg { + u16 reg; + u8 val; +}; + -+/* wizard: MIPI 1928x1088 RAW12 Linear 30fps 960Mbps */ ++/* wizard: MIPI 1920x1080 RAW12 Linear 30fps 600Mbps XCLK=25MHz */ +static const struct ov2775_reg ov2775_regs_wizard[] = { +{0x3013, 0x01}, // s/w reset +{OV2775_DELAY, 10}, // Wait 10ms -+{0x3000, 0x02}, -+{0x3001, 0x28}, -+{0x3002, 0x03}, ++{0x3000, 0x03}, ++{0x3001, 0x18}, ++{0x3002, 0x01}, +{0x3003, 0x01}, -+{0x3004, 0x02}, -+{0x3005, 0x26}, ++{0x3004, 0x03}, ++{0x3005, 0x1c}, +{0x3006, 0x00}, +{0x3007, 0x07}, +{0x3008, 0x01}, @@ -5752,7 +10164,7 @@ index 0000000..1cdfb50 +{0x300f, 0x00}, +{0x3012, 0x00}, +{0x3013, 0x00}, -+{0x3014, 0xc4}, ++{0x3014, 0x44}, +{0x3015, 0x00}, +{0x3017, 0x00}, +{0x3018, 0x00}, @@ -6039,7 +10451,7 @@ index 0000000..1cdfb50 +{0x320d, 0x1e}, +{0x320e, 0x30}, +{0x320f, 0x2d}, -+{0x3210, OV2775_DT}, ++{0x3210, 0x2c}, +{0x3211, 0x2b}, +{0x3212, 0x2a}, +{0x3213, 0x24}, @@ -6069,7 +10481,7 @@ index 0000000..1cdfb50 +{0x3251, 0x00}, +{0x3252, 0x20}, +#ifdef OV2775_DISPLAY_PATTERN_COLOR_BAR -+{0x3253, 0x80}, ++{0x3253, 0xC0}, +#else +{0x3253, 0x00}, +#endif @@ -7533,9 +11945,9 @@ index 0000000..1cdfb50 +{0x30a9, 0x04}, +{0x30aa, 0x00}, +{0x30ab, 0x04}, -+{0x30ac, 0x07}, ++{0x30ac, 0x07}, /* OV2775_MAX_WIDTH */ +{0x30ad, 0x88}, -+{0x30ae, 0x04}, ++{0x30ae, 0x04}, /* OV2775_MAX_HEIGHT */ +{0x30af, 0x40}, +{0x30b0, 0x0d}, +{0x30b1, 0xde}, @@ -7550,16 +11962,1263 @@ index 0000000..1cdfb50 +{0x3250, 0xf7}, +{0x3012, 0x01}, +}; -diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.c b/drivers/media/i2c/soc_camera/ov490_ov10640.c +diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.c b/drivers/media/i2c/soc_camera/ov490_ov10640.c +new file mode 100644 +index 0000000..812f367 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov490_ov10640.c +@@ -0,0 +1,1133 @@ ++/* ++ * OmniVision ov490-ov10640 sensor camera driver ++ * ++ * Copyright (C) 2016-2018 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "max9286.h" ++#include "ov490_ov10640.h" ++ ++#define OV490_I2C_ADDR 0x24 ++ ++#define OV490_PID 0x300a ++#define OV490_VER 0x300b ++#define OV490_VERSION_REG 0x0490 ++#define OV490_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) ++ ++#define OV490_ISP_HSIZE_LOW 0x60 ++#define OV490_ISP_HSIZE_HIGH 0x61 ++#define OV490_ISP_VSIZE_LOW 0x62 ++#define OV490_ISP_VSIZE_HIGH 0x63 ++ ++struct ov490_priv { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct media_pad pad; ++ struct v4l2_rect rect; ++ int max_width; ++ int max_height; ++ char is_fixed_sensor; ++ int init_complete; ++ u8 id[6]; ++ int exposure; ++ int gain; ++ int autogain; ++ int red; ++ int green_r; ++ int green_b; ++ int blue; ++ int awb; ++ int dvp_order; ++ /* serializers */ ++ int max9286_addr; ++ int max9271_addr; ++ int ti9x4_addr; ++ int ti9x3_addr; ++ int port; ++ int gpio_resetb; ++ int active_low_resetb; ++ int gpio_fsin; ++}; ++ ++static int conf_link; ++module_param(conf_link, int, 0644); ++MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); ++ ++static int dvp_order; ++module_param(dvp_order, int, 0644); ++MODULE_PARM_DESC(dvp_order, " DVP bus bits order"); ++ ++static int max_width; ++module_param(max_width, int, 0644); ++MODULE_PARM_DESC(max_width, " Fixed sensor width"); ++ ++static int max_height; ++module_param(max_height, int, 0644); ++MODULE_PARM_DESC(max_height, " Fixed sensor height"); ++ ++static inline struct ov490_priv *to_ov490(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov490_priv, sd); ++} ++ ++static void ov490_s_port(struct i2c_client *client, int fwd_en) ++{ ++ struct ov490_priv *priv = to_ov490(client); ++ int tmp_addr; ++ ++ if (priv->max9286_addr) { ++ tmp_addr = client->addr; ++ client->addr = priv->max9286_addr; /* Deserializer I2C address */ ++ reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */ ++ usleep_range(5000, 5500); /* wait 5ms */ ++ client->addr = tmp_addr; ++ }; ++} ++ ++static void ov490_reset(struct i2c_client *client) ++{ ++ struct ov490_priv *priv = to_ov490(client); ++ int tmp_addr; ++ ++ if (priv->max9286_addr) { ++ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) ++ return; ++ ++ tmp_addr = client->addr; ++ /* get out from sensor reset */ ++ client->addr = priv->max9271_addr; /* MAX9271 I2C address */ ++ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | ++ (priv->active_low_resetb ? 0 : BIT(priv->gpio_resetb))); /* set GPIOn value to reset */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | ++ (priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0)); /* set GPIOn value to un-reset */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ client->addr = tmp_addr; ++ } ++ ++ if (priv->ti9x4_addr) { ++ client->addr = priv->ti9x4_addr; /* TI9x4 I2C address */ ++ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x6e, 0x8a); /* set GPIO1 value to reset */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x6e, 0x9a); /* set GPIO1 value to un-reset */ ++ } ++} ++ ++static int ov490_set_regs(struct i2c_client *client, ++ const struct ov490_reg *regs, int nr_regs) ++{ ++ int i; ++ ++ for (i = 0; i < nr_regs; i++) { ++ if (reg16_write(client, regs[i].reg, regs[i].val)) { ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, regs[i].reg, regs[i].val); ++ } ++ } ++ ++ return 0; ++} ++ ++static u8 ov490_ov10640_read(struct i2c_client *client, u16 addr) ++{ ++ u8 reg_val = 0; ++ ++ reg16_write(client, 0xFFFD, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x5000, 0x01); /* read operation */ ++ reg16_write(client, 0x5001, addr >> 8); ++ reg16_write(client, 0x5002, addr & 0xff); ++ reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x00C0, 0xc1); ++ reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(1000, 1500); /* wait 1 ms */ ++ reg16_read(client, 0x5000, ®_val); ++ ++ return reg_val; ++} ++ ++static void ov490_ov10640_write(struct i2c_client *client, u16 addr, u8 val) ++{ ++ reg16_write(client, 0xFFFD, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x5000, 0x00); /* write operation */ ++ reg16_write(client, 0x5001, addr >> 8); ++ reg16_write(client, 0x5002, addr & 0xff); ++ reg16_write(client, 0x5003, val); ++ reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x00C0, 0xc1); ++} ++ ++static int ov490_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++static int ov490_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ ++ if (format->pad) ++ return -EINVAL; ++ ++ mf->width = priv->rect.width; ++ mf->height = priv->rect.height; ++ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov490_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct v4l2_mbus_framefmt *mf = &format->format; ++ ++ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->field = V4L2_FIELD_NONE; ++ ++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) ++ cfg->try_fmt = *mf; ++ ++ return 0; ++} ++ ++static int ov490_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->pad || code->index > 0) ++ return -EINVAL; ++ ++ code->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ ++ return 0; ++} ++ ++static int ov490_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ ++ memcpy(edid->edid, priv->id, 6); ++ ++ edid->edid[6] = 0xff; ++ edid->edid[7] = client->addr; ++ edid->edid[8] = OV490_VERSION_REG >> 8; ++ edid->edid[9] = OV490_VERSION_REG & 0xff; ++ ++ return 0; ++} ++ ++static int ov490_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct v4l2_rect *rect = &sel->r; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || ++ sel->target != V4L2_SEL_TGT_CROP) ++ return -EINVAL; ++ ++ rect->left = ALIGN(rect->left, 2); ++ rect->top = ALIGN(rect->top, 2); ++ rect->width = ALIGN(rect->width, 2); ++ rect->height = ALIGN(rect->height, 2); ++ ++ if ((rect->left + rect->width > priv->max_width) || ++ (rect->top + rect->height > priv->max_height)) ++ *rect = priv->rect; ++ ++ priv->rect.left = rect->left; ++ priv->rect.top = rect->top; ++ priv->rect.width = rect->width; ++ priv->rect.height = rect->height; ++ ++ return 0; ++} ++ ++static int ov490_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = priv->max_width; ++ sel->r.height = priv->max_height; ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = priv->max_width; ++ sel->r.height = priv->max_height; ++ return 0; ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->rect; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ov490_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ cfg->type = V4L2_MBUS_CSI2; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov490_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val = 0; ++ ++ ret = reg16_read(client, (u16)reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int ov490_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ ret = reg16_write(client, (u16)reg->reg, (u8)reg->val); ++ if ((u8)reg->reg == 0xFFFD) ++ usleep_range(100, 150); /* wait 100 us */ ++ if ((u8)reg->reg == 0xFFFE) ++ usleep_range(100, 150); /* wait 100 us */ ++ return ret; ++} ++#endif ++ ++static struct v4l2_subdev_core_ops ov490_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov490_g_register, ++ .s_register = ov490_s_register, ++#endif ++}; ++ ++static int ov490_s_gamma(int a, int ref) ++{ ++ if ((a + ref) > 0xff) ++ return 0xff; ++ ++ if ((a + ref) < 0) ++ return 0; ++ ++ return a + ref; ++} ++ ++static int ov490_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ int ret = -EINVAL; ++ ++ if (!priv->init_complete) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ /* SDE (rough) brightness */ ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); ++ ret |= reg16_write(client, 0x5001, ctrl->val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xf1); ++ break; ++ case V4L2_CID_CONTRAST: ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xfd); ++ break; ++ case V4L2_CID_SATURATION: ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xf3); ++ break; ++ case V4L2_CID_HUE: ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xf5); ++ break; ++ case V4L2_CID_GAMMA: ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ov490_s_gamma(ctrl->val, 0x12)); ++ ret |= reg16_write(client, 0x5001, ov490_s_gamma(ctrl->val, 0x20)); ++ ret |= reg16_write(client, 0x5002, ov490_s_gamma(ctrl->val, 0x3b)); ++ ret |= reg16_write(client, 0x5003, ov490_s_gamma(ctrl->val, 0x5d)); ++ ret |= reg16_write(client, 0x5004, ov490_s_gamma(ctrl->val, 0x6a)); ++ ret |= reg16_write(client, 0x5005, ov490_s_gamma(ctrl->val, 0x76)); ++ ret |= reg16_write(client, 0x5006, ov490_s_gamma(ctrl->val, 0x81)); ++ ret |= reg16_write(client, 0x5007, ov490_s_gamma(ctrl->val, 0x8b)); ++ ret |= reg16_write(client, 0x5008, ov490_s_gamma(ctrl->val, 0x96)); ++ ret |= reg16_write(client, 0x5009, ov490_s_gamma(ctrl->val, 0x9e)); ++ ret |= reg16_write(client, 0x500a, ov490_s_gamma(ctrl->val, 0xae)); ++ ret |= reg16_write(client, 0x500b, ov490_s_gamma(ctrl->val, 0xbc)); ++ ret |= reg16_write(client, 0x500c, ov490_s_gamma(ctrl->val, 0xcf)); ++ ret |= reg16_write(client, 0x500d, ov490_s_gamma(ctrl->val, 0xde)); ++ ret |= reg16_write(client, 0x500e, ov490_s_gamma(ctrl->val, 0xec)); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xf9); ++ break; ++ case V4L2_CID_SHARPNESS: ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xfb); ++ break; ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_GAIN: ++ case V4L2_CID_EXPOSURE: ++ if (ctrl->id == V4L2_CID_AUTOGAIN) ++ priv->autogain = ctrl->val; ++ if (ctrl->id == V4L2_CID_GAIN) ++ priv->gain = ctrl->val; ++ if (ctrl->id == V4L2_CID_EXPOSURE) ++ priv->exposure = ctrl->val; ++ ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, !priv->autogain); ++ ret |= reg16_write(client, 0x5001, priv->exposure >> 8); ++ ret |= reg16_write(client, 0x5002, priv->exposure & 0xff); ++ ret |= reg16_write(client, 0x5003, priv->exposure >> 8); ++ ret |= reg16_write(client, 0x5004, priv->exposure & 0xff); ++ ret |= reg16_write(client, 0x5005, priv->exposure >> 8); ++ ret |= reg16_write(client, 0x5006, priv->exposure & 0xff); ++ ret |= reg16_write(client, 0x5007, priv->gain >> 8); ++ ret |= reg16_write(client, 0x5008, priv->gain & 0xff); ++ ret |= reg16_write(client, 0x5009, priv->gain >> 8); ++ ret |= reg16_write(client, 0x500a, priv->gain & 0xff); ++ ret |= reg16_write(client, 0x500b, priv->gain >> 8); ++ ret |= reg16_write(client, 0x500c, priv->gain & 0xff); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xea); ++ break; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ case V4L2_CID_RED_BALANCE: ++ case V4L2_CID_BLUE_BALANCE: ++ if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE) ++ priv->awb = ctrl->val; ++ if (ctrl->id == V4L2_CID_RED_BALANCE) { ++ priv->red = ctrl->val; ++ priv->red <<= 8; ++ priv->green_r = priv->red / 2; ++ } ++ if (ctrl->id == V4L2_CID_BLUE_BALANCE) { ++ priv->blue = ctrl->val; ++ priv->blue <<= 8; ++ priv->green_b = priv->blue / 2; ++ } ++ ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, !priv->awb); ++ ret |= reg16_write(client, 0x5001, priv->red >> 8); ++ ret |= reg16_write(client, 0x5002, priv->red & 0xff); ++ ret |= reg16_write(client, 0x5003, priv->green_r >> 8); ++ ret |= reg16_write(client, 0x5004, priv->green_r & 0xff); ++ ret |= reg16_write(client, 0x5005, priv->green_b >> 8); ++ ret |= reg16_write(client, 0x5006, priv->green_b & 0xff); ++ ret |= reg16_write(client, 0x5007, priv->blue >> 8); ++ ret |= reg16_write(client, 0x5008, priv->blue & 0xff); ++ ret |= reg16_write(client, 0x5009, priv->red >> 8); ++ ret |= reg16_write(client, 0x500a, priv->red & 0xff); ++ ret |= reg16_write(client, 0x500b, priv->green_r >> 8); ++ ret |= reg16_write(client, 0x500c, priv->green_r & 0xff); ++ ret |= reg16_write(client, 0x500d, priv->green_b >> 8); ++ ret |= reg16_write(client, 0x500e, priv->green_b & 0xff); ++ ret |= reg16_write(client, 0x500f, priv->blue >> 8); ++ ret |= reg16_write(client, 0x5010, priv->blue & 0xff); ++ ret |= reg16_write(client, 0x5011, priv->red >> 8); ++ ret |= reg16_write(client, 0x5012, priv->red & 0xff); ++ ret |= reg16_write(client, 0x5013, priv->green_r >> 8); ++ ret |= reg16_write(client, 0x5014, priv->green_r & 0xff); ++ ret |= reg16_write(client, 0x5015, priv->green_b >> 8); ++ ret |= reg16_write(client, 0x5016, priv->green_b & 0xff); ++ ret |= reg16_write(client, 0x5017, priv->blue >> 8); ++ ret |= reg16_write(client, 0x5018, priv->blue & 0xff); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xeb); ++ break; ++ case V4L2_CID_HFLIP: ++#if 1 ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0x5001, 0x00); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xdc); ++#else ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128 ++ ret |= reg16_write(client, 0x5001, 0x31); ++ ret |= reg16_write(client, 0x5002, 0x28); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 0); ++ val |= (ctrl->val << 0); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128 ++ ret |= reg16_write(client, 0x5001, 0x31); ++ ret |= reg16_write(client, 0x5002, 0x28); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291 ++ ret |= reg16_write(client, 0x5001, 0x32); ++ ret |= reg16_write(client, 0x5002, 0x91); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 1); ++ val |= (ctrl->val << 1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291 ++ ret |= reg16_write(client, 0x5001, 0x32); ++ ret |= reg16_write(client, 0x5002, 0x91); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090 ++ ret |= reg16_write(client, 0x5001, 0x30); ++ ret |= reg16_write(client, 0x5002, 0x90); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 2); ++ val |= (ctrl->val << 2); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090 ++ ret |= reg16_write(client, 0x5001, 0x30); ++ ret |= reg16_write(client, 0x5002, 0x90); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++#endif ++ break; ++ case V4L2_CID_VFLIP: ++#if 1 ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, ctrl->val); ++ ret |= reg16_write(client, 0x5001, 0x01); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xdc); ++#else ++ ret = reg16_write(client, 0xFFFD, 0x80); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128 ++ ret |= reg16_write(client, 0x5001, 0x31); ++ ret |= reg16_write(client, 0x5002, 0x28); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 1); ++ val |= (ctrl->val << 1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128 ++ ret |= reg16_write(client, 0x5001, 0x31); ++ ret |= reg16_write(client, 0x5002, 0x28); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291 ++ ret |= reg16_write(client, 0x5001, 0x32); ++ ret |= reg16_write(client, 0x5002, 0x91); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 2); ++ val |= (ctrl->val << 2); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291 ++ ret |= reg16_write(client, 0x5001, 0x32); ++ ret |= reg16_write(client, 0x5002, 0x91); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090 ++ ret |= reg16_write(client, 0x5001, 0x30); ++ ret |= reg16_write(client, 0x5002, 0x90); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_read(client, 0x5000, &val); ++ val &= ~(0x1 << 3); ++ val |= (ctrl->val << 3); ++ ret |= reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090 ++ ret |= reg16_write(client, 0x5001, 0x30); ++ ret |= reg16_write(client, 0x5002, 0x90); ++ ret |= reg16_write(client, 0x5003, val); ++ ret |= reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ ret |= reg16_write(client, 0x00C0, 0xc1); ++#endif ++ break; ++ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: ++ ret = 0; ++ break; ++ } ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov490_ctrl_ops = { ++ .s_ctrl = ov490_s_ctrl, ++}; ++ ++static struct v4l2_subdev_video_ops ov490_video_ops = { ++ .s_stream = ov490_s_stream, ++ .g_mbus_config = ov490_g_mbus_config, ++}; ++ ++static const struct v4l2_subdev_pad_ops ov490_subdev_pad_ops = { ++ .get_edid = ov490_get_edid, ++ .enum_mbus_code = ov490_enum_mbus_code, ++ .get_selection = ov490_get_selection, ++ .set_selection = ov490_set_selection, ++ .get_fmt = ov490_get_fmt, ++ .set_fmt = ov490_set_fmt, ++}; ++ ++static struct v4l2_subdev_ops ov490_subdev_ops = { ++ .core = &ov490_core_ops, ++ .video = &ov490_video_ops, ++ .pad = &ov490_subdev_pad_ops, ++}; ++ ++static void ov490_otp_id_read(struct i2c_client *client) ++{ ++ struct ov490_priv *priv = to_ov490(client); ++ int i; ++ int otp_bank0_allzero = 1; ++ ++#if 0 ++ /* read camera id from ov490 OTP memory */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x28); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0xE084, 0x40); /* manual mode, bank#0 */ ++ reg16_write(client, 0xE081, 1); /* start OTP read */ ++ ++ usleep_range(25000, 26000); /* wait 25 ms */ ++ ++ for (i = 0; i < 6; i++) ++ reg16_read(client, 0xe000 + i + 4, &priv->id[i]); ++#else ++ /* read camera id from ov10640 OTP memory */ ++ ov490_ov10640_write(client, 0x349C, 1); ++ usleep_range(25000, 25500); /* wait 25 ms */ ++ ++ for (i = 0; i < 6; i++) { ++ /* first 6 bytes are equal on all ov10640 */ ++ priv->id[i] = ov490_ov10640_read(client, 0x349e + i + 6); ++ if (priv->id[i]) ++ otp_bank0_allzero = 0; ++ } ++ ++ if (otp_bank0_allzero) { ++ ov490_ov10640_write(client, 0x3495, 0x41); /* bank#1 */ ++ ov490_ov10640_write(client, 0x349C, 1); ++ usleep_range(25000, 25500); /* wait 25 ms */ ++ ++ for (i = 0; i < 6; i++) ++ priv->id[i] = ov490_ov10640_read(client, 0x34ae + i); ++ } ++#endif ++} ++ ++static ssize_t ov490_otp_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov490_priv *priv = to_ov490(client); ++ ++ return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++} ++ ++static DEVICE_ATTR(otp_id_ov490, S_IRUGO, ov490_otp_id_show, NULL); ++ ++static int ov490_initialize(struct i2c_client *client) ++{ ++ struct ov490_priv *priv = to_ov490(client); ++ u8 val = 0; ++ u8 pid = 0, ver = 0; ++ int ret = 0, timeout, retry_timeout = 3; ++ ++ if (priv->is_fixed_sensor) { ++ dev_info(&client->dev, "ov490/ov10640 fixed-sensor res %dx%d\n", priv->max_width, priv->max_height); ++ return 0; ++ } ++ ++ ov490_s_port(client, 1); ++ ++ /* check and show product ID and manufacturer ID */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_read(client, OV490_PID, &pid); ++ reg16_read(client, OV490_VER, &ver); ++ ++ if (OV490_VERSION(pid, ver) != OV490_VERSION_REG) { ++ dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ if (unlikely(conf_link)) ++ goto out; ++ ++again: ++ /* Check if firmware booted by reading stream-on status */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x29); ++ usleep_range(100, 150); /* wait 100 us */ ++ for (timeout = 300; timeout > 0; timeout--) { ++ reg16_read(client, 0xd000, &val); ++ if (val == 0x0c) ++ break; ++ mdelay(1); ++ } ++ ++ /* wait firmware apps started by reading OV10640 ID */ ++ for (;timeout > 0; timeout--) { ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x5000, 0x01); ++ reg16_write(client, 0x5001, 0x30); ++ reg16_write(client, 0x5002, 0x0a); ++ reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0xC0, 0xc1); ++ reg16_write(client, 0xFFFE, 0x19); ++ usleep_range(1000, 1500); /* wait 1 ms */ ++ reg16_read(client, 0x5000, &val); ++ if (val == 0xa6) ++ break; ++ mdelay(1); ++ } ++ ++ if (!timeout) { ++ dev_err(&client->dev, "Timeout firmware boot wait, retrying\n"); ++ /* reset OV10640 using RESETB pin controlled by OV490 GPIO0 */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x80); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x0050, 0x01); ++ reg16_write(client, 0x0054, 0x01); ++ reg16_write(client, 0x0058, 0x00); ++ mdelay(10); ++ reg16_write(client, 0x0058, 0x01); ++ /* reset OV490 using RESETB pin controlled by serializer */ ++ ov490_reset(client); ++ if (retry_timeout--) ++ goto again; ++ } ++ ++ /* read resolution used by current firmware */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x82); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_read(client, OV490_ISP_HSIZE_HIGH, &val); ++ priv->max_width = val; ++ reg16_read(client, OV490_ISP_HSIZE_LOW, &val); ++ priv->max_width = (priv->max_width << 8) | val; ++ reg16_read(client, OV490_ISP_VSIZE_HIGH, &val); ++ priv->max_height = val; ++ reg16_read(client, OV490_ISP_VSIZE_LOW, &val); ++ priv->max_height = (priv->max_height << 8) | val; ++ /* Program wizard registers */ ++ ov490_set_regs(client, ov490_regs_wizard, ARRAY_SIZE(ov490_regs_wizard)); ++ /* Set DVP bit swap */ ++ reg16_write(client, 0xFFFD, 0x80); ++ reg16_write(client, 0xFFFE, 0x28); ++ usleep_range(100, 150); /* wait 100 us */ ++ reg16_write(client, 0x6009, priv->dvp_order << 4); ++ /* Read OTP IDs */ ++ ov490_otp_id_read(client); ++ ++out: ++ dev_info(&client->dev, "ov490/ov10640 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ pid, ver, priv->max_width, priv->max_height, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++err: ++ ov490_s_port(client, 0); ++ ++ return ret; ++} ++ ++static int ov490_parse_dt(struct device_node *np, struct ov490_priv *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ int i; ++ const char *fixed_sensor; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ int tmp_addr = 0; ++ ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; ++ ++ of_node_put(endpoint); ++ ++ of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order); ++ ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; ++ ++ if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) { ++ if (of_property_read_u32(rendpoint->parent->parent, "maxim,resetb-gpio", &priv->gpio_resetb)) { ++ priv->gpio_resetb = -1; ++ } else { ++ if (of_property_read_bool(rendpoint->parent->parent, "maxim,resetb-active-high")) ++ priv->active_low_resetb = false; ++ else ++ priv->active_low_resetb = true; ++ } ++ break; ++ } ++ ++ if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && ++ !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") && ++ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) && ++ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) ++ break; ++ } ++ ++ if (!priv->max9286_addr && !priv->ti9x4_addr) { ++ dev_err(&client->dev, "deserializer does not present for OV490\n"); ++ return -EINVAL; ++ } ++ ++ ov490_s_port(client, 1); ++ ++ /* setup I2C translator address */ ++ tmp_addr = client->addr; ++ if (priv->max9286_addr) { ++ client->addr = priv->max9271_addr; /* Serializer I2C address */ ++ ++ reg8_write(client, 0x09, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x0A, OV490_I2C_ADDR << 1); /* Sensor native I2C address */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ }; ++ if (priv->ti9x4_addr) { ++ client->addr = priv->ti9x4_addr; /* Deserializer I2C address */ ++ ++ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ ++ usleep_range(2000, 2500); /* wait 2ms */ ++ reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ ++ reg8_write(client, 0x5d, OV490_I2C_ADDR << 1); /* Sensor native I2C address */ ++ ++ reg8_write(client, 0x6e, 0x9a); /* GPIO0 - fsin, GPIO1 - resetb */ ++ } ++ client->addr = tmp_addr; ++ ++ if (!of_property_read_string(np, "maxim,fixed-sensor", &fixed_sensor) && ++ strcmp(fixed_sensor, "ov490") == 0) { ++ if (of_property_read_u32(np, "maxim,width", &priv->max_width)) ++ priv->max_width = 1280; ++ ++ if (of_property_read_u32(np, "maxim,height", &priv->max_height)) ++ priv->max_height = 966; ++ ++ priv->is_fixed_sensor = true; ++ } ++ ++ /* module params override dts */ ++ if (dvp_order) ++ priv->dvp_order = dvp_order; ++ if (max_width && max_height) { ++ priv->max_width = max_width; ++ priv->max_height = max_height; ++ priv->is_fixed_sensor = true; ++ } ++ ++ return 0; ++} ++ ++static int ov490_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov490_priv *priv; ++ struct v4l2_ctrl *ctrl; ++ int ret; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&priv->sd, client, &ov490_subdev_ops); ++ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ priv->exposure = 0x100; ++ priv->gain = 0x100; ++ priv->autogain = 1; ++ priv->red = 0x400; ++ priv->blue = 0x400; ++ priv->green_r = priv->red / 2; ++ priv->green_b = priv->blue / 2; ++ priv->awb = 1; ++ v4l2_ctrl_handler_init(&priv->hdl, 4); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 16, 1, 7); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 16, 1, 7); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 7, 1, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_HUE, 0, 23, 1, 12); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_GAMMA, -128, 128, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_SHARPNESS, 0, 10, 1, 3); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, priv->autogain); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_RED_BALANCE, 2, 0xf, 1, priv->red >> 8); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_BLUE_BALANCE, 2, 0xf, 1, priv->blue >> 8); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); ++ if (ctrl) ++ ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; ++ priv->sd.ctrl_handler = &priv->hdl; ++ ++ ret = priv->hdl.error; ++ if (ret) ++ goto cleanup; ++ ++ v4l2_ctrl_handler_setup(&priv->hdl); ++ ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ priv->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&priv->sd.entity, 1, &priv->pad); ++ if (ret < 0) ++ goto cleanup; ++ ++ ret = ov490_parse_dt(client->dev.of_node, priv); ++ if (ret) ++ goto cleanup; ++ ++ ret = ov490_initialize(client); ++ if (ret < 0) ++ goto cleanup; ++ ++ priv->rect.left = 0; ++ priv->rect.top = 0; ++ priv->rect.width = priv->max_width; ++ priv->rect.height = priv->max_height; ++ ++ ret = v4l2_async_register_subdev(&priv->sd); ++ if (ret) ++ goto cleanup; ++ ++ if (device_create_file(&client->dev, &dev_attr_otp_id_ov490) != 0) { ++ dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); ++ goto cleanup; ++ } ++ ++ priv->init_complete = 1; ++ ++ return 0; ++ ++cleanup: ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++#ifdef CONFIG_SOC_CAMERA_OV490_OV10640 ++ v4l_err(client, "failed to probe @ 0x%02x (%s)\n", ++ client->addr, client->adapter->name); ++#endif ++ return ret; ++} ++ ++static int ov490_remove(struct i2c_client *client) ++{ ++ struct ov490_priv *priv = i2c_get_clientdata(client); ++ ++ device_remove_file(&client->dev, &dev_attr_otp_id_ov490); ++ v4l2_async_unregister_subdev(&priv->sd); ++ media_entity_cleanup(&priv->sd.entity); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ v4l2_device_unregister_subdev(&priv->sd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SOC_CAMERA_OV490_OV10640 ++static const struct i2c_device_id ov490_id[] = { ++ { "ov490-ov10640", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov490_id); ++ ++static const struct of_device_id ov490_of_ids[] = { ++ { .compatible = "ovti,ov490-ov10640", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ov490_of_ids); ++ ++static struct i2c_driver ov490_i2c_driver = { ++ .driver = { ++ .name = "ov490-ov10640", ++ .of_match_table = ov490_of_ids, ++ }, ++ .probe = ov490_probe, ++ .remove = ov490_remove, ++ .id_table = ov490_id, ++}; ++ ++module_i2c_driver(ov490_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OV490-OV10640"); ++MODULE_AUTHOR("Vladimir Barinov"); ++MODULE_LICENSE("GPL"); ++#endif +diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.h b/drivers/media/i2c/soc_camera/ov490_ov10640.h +new file mode 100644 +index 0000000..b22e93e +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov490_ov10640.h +@@ -0,0 +1,102 @@ ++/* ++ * OmniVision ov490-ov10640 sensor camera wizard 1280x1080@30/UYVY/BT601/8bit ++ * ++ * Copyright (C) 2016-2017 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++//#define OV490_DISPLAY_PATTERN ++ ++struct ov490_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++static const struct ov490_reg ov490_regs_wizard[] = { ++/* The following registers should match firmware */ ++{0xfffd, 0x80}, ++{0xfffe, 0x82}, ++{0x0071, 0x11}, ++{0x0075, 0x11}, ++{0xfffe, 0x29}, ++{0x6010, 0x01}, ++/* ov490 EMB line disable in YUV and RAW data, NOTE: EMB line is still used in ISP and sensor */ ++{0xe000, 0x14}, ++#if 0 /* do not disable EMB line in ISP! */ ++{0x4017, 0x00}, ++#endif ++{0xfffe, 0x28}, ++{0x6000, 0x04}, ++{0x6004, 0x00}, ++{0x6008, 0x00}, // PCLK polarity - useless due to silicon bug -> use 0x808000bb register ++{0xfffe, 0x80}, ++{0x0091, 0x00}, ++{0x00bb, 0x1d}, // bit[3]=0 - PCLK polarity workaround ++/* ov10640 EMB line disable */ ++#if 0 /* do not disable EMB line in sensor! */ ++{0xfffe, 0x19}, ++{0x5000, 0x00}, ++{0x5001, 0x30}, ++{0x5002, 0x91}, ++{0x5003, 0x08}, ++{0xfffe, 0x80}, ++{0x00c0, 0xc1}, ++#endif ++/* Ov490 FSIN: app_fsin_from_fsync */ ++{0xfffe, 0x85}, ++{0x0008, 0x00}, ++{0x0009, 0x01}, ++{0x000A, 0x05}, // fsin0 src ++{0x000B, 0x00}, ++{0x0030, 0x02}, // fsin0_delay ++{0x0031, 0x00}, ++{0x0032, 0x00}, ++{0x0033, 0x00}, ++{0x0038, 0x02}, // fsin1_delay ++{0x0039, 0x00}, ++{0x003A, 0x00}, ++{0x003B, 0x00}, ++{0x0070, 0x2C}, // fsin0_length ++{0x0071, 0x01}, ++{0x0072, 0x00}, ++{0x0073, 0x00}, ++{0x0074, 0x64}, // fsin1_length ++{0x0075, 0x00}, ++{0x0076, 0x00}, ++{0x0077, 0x00}, ++{0x0000, 0x14}, ++{0x0001, 0x00}, ++{0x0002, 0x00}, ++{0x0003, 0x00}, ++{0x0004, 0x32}, // load fsin0,load fsin1,load other, it will be cleared automatically. ++{0x0005, 0x00}, ++{0x0006, 0x00}, ++{0x0007, 0x00}, ++{0xfffe, 0x80}, ++{0x0081, 0x00}, // 03;SENSOR FSIN ++/* ov10640 FSIN */ ++{0xfffe, 0x19}, ++{0x5000, 0x00}, ++{0x5001, 0x30}, ++{0x5002, 0x8c}, ++{0x5003, 0xb2}, ++{0xfffe, 0x80}, ++{0x00c0, 0xc1}, ++/* ov10640 HFLIP=1 by default */ ++{0xfffe, 0x19}, ++{0x5000, 0x01}, ++{0x5001, 0x00}, ++{0xfffe, 0x80}, ++{0x00c0, 0xdc}, ++#ifdef OV490_DISPLAY_PATTERN ++{0xfffd, 0x80}, ++{0xfffe, 0x19}, ++{0x5000, 0x02}, ++{0xfffe, 0x80}, ++{0x00c0, 0xd6}, ++#endif ++}; +diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.c b/drivers/media/i2c/soc_camera/ov495_ov2775.c new file mode 100644 -index 0000000..812f367 +index 0000000..a371e22 --- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov490_ov10640.c -@@ -0,0 +1,1133 @@ ++++ b/drivers/media/i2c/soc_camera/ov495_ov2775.c +@@ -0,0 +1,650 @@ +/* -+ * OmniVision ov490-ov10640 sensor camera driver ++ * OmniVision ov495-ov2775 sensor camera driver + * -+ * Copyright (C) 2016-2018 Cogent Embedded, Inc. ++ * Copyright (C) 2017 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -7578,40 +13237,32 @@ index 0000000..812f367 +#include +#include + -+#include "max9286.h" -+#include "ov490_ov10640.h" ++#include "ov495_ov2775.h" + -+#define OV490_I2C_ADDR 0x24 ++#define OV495_I2C_ADDR 0x24 + -+#define OV490_PID 0x300a -+#define OV490_VER 0x300b -+#define OV490_VERSION_REG 0x0490 -+#define OV490_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) ++#define OV495_PID 0x300a ++#define OV495_VER 0x300b ++#define OV495_VERSION_REG 0x0495 ++#define OV495_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) + -+#define OV490_ISP_HSIZE_LOW 0x60 -+#define OV490_ISP_HSIZE_HIGH 0x61 -+#define OV490_ISP_VSIZE_LOW 0x62 -+#define OV490_ISP_VSIZE_HIGH 0x63 ++#define OV495_ISP_HSIZE_LOW 0x60 ++#define OV495_ISP_HSIZE_HIGH 0x61 ++#define OV495_ISP_VSIZE_LOW 0x62 ++#define OV495_ISP_VSIZE_HIGH 0x63 + -+struct ov490_priv { ++struct ov495_priv { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; + struct v4l2_rect rect; + int max_width; + int max_height; -+ char is_fixed_sensor; + int init_complete; + u8 id[6]; + int exposure; + int gain; + int autogain; -+ int red; -+ int green_r; -+ int green_b; -+ int blue; -+ int awb; -+ int dvp_order; + /* serializers */ + int max9286_addr; + int max9271_addr; @@ -7619,79 +13270,28 @@ index 0000000..812f367 + int ti9x3_addr; + int port; + int gpio_resetb; -+ int active_low_resetb; + int gpio_fsin; -+}; -+ -+static int conf_link; -+module_param(conf_link, int, 0644); -+MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); -+ -+static int dvp_order; -+module_param(dvp_order, int, 0644); -+MODULE_PARM_DESC(dvp_order, " DVP bus bits order"); -+ -+static int max_width; -+module_param(max_width, int, 0644); -+MODULE_PARM_DESC(max_width, " Fixed sensor width"); + -+static int max_height; -+module_param(max_height, int, 0644); -+MODULE_PARM_DESC(max_height, " Fixed sensor height"); ++}; + -+static inline struct ov490_priv *to_ov490(const struct i2c_client *client) -+{ -+ return container_of(i2c_get_clientdata(client), struct ov490_priv, sd); -+} ++static int force_conf_link; + -+static void ov490_s_port(struct i2c_client *client, int fwd_en) ++static __init int ov495_force_conf_link(char *str) +{ -+ struct ov490_priv *priv = to_ov490(client); -+ int tmp_addr; -+ -+ if (priv->max9286_addr) { -+ tmp_addr = client->addr; -+ client->addr = priv->max9286_addr; /* Deserializer I2C address */ -+ reg8_write(client, 0x0a, fwd_en ? 0x11 << priv->port : 0); /* Enable/disable reverse/forward control for this port */ -+ usleep_range(5000, 5500); /* wait 5ms */ -+ client->addr = tmp_addr; -+ }; ++ /* force configuration link */ ++ /* used only if robust firmware flashing required (f.e. recovery) */ ++ force_conf_link = 1; ++ return 0; +} ++early_param("force_conf_link", ov495_force_conf_link); + -+static void ov490_reset(struct i2c_client *client) ++static inline struct ov495_priv *to_ov495(const struct i2c_client *client) +{ -+ struct ov490_priv *priv = to_ov490(client); -+ int tmp_addr; -+ -+ if (priv->max9286_addr) { -+ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) -+ return; -+ -+ tmp_addr = client->addr; -+ /* get out from sensor reset */ -+ client->addr = priv->max9271_addr; /* MAX9271 I2C address */ -+ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | -+ (priv->active_low_resetb ? 0 : BIT(priv->gpio_resetb))); /* set GPIOn value to reset */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | -+ (priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0)); /* set GPIOn value to un-reset */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ client->addr = tmp_addr; -+ } -+ -+ if (priv->ti9x4_addr) { -+ client->addr = priv->ti9x4_addr; /* TI9x4 I2C address */ -+ -+ reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x6e, 0x8a); /* set GPIO1 value to reset */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ reg8_write(client, 0x6e, 0x9a); /* set GPIO1 value to un-reset */ -+ } ++ return container_of(i2c_get_clientdata(client), struct ov495_priv, sd); +} + -+static int ov490_set_regs(struct i2c_client *client, -+ const struct ov490_reg *regs, int nr_regs) ++static int ov495_set_regs(struct i2c_client *client, ++ const struct ov495_reg *regs, int nr_regs) +{ + int i; + @@ -7705,54 +13305,18 @@ index 0000000..812f367 + return 0; +} + -+static u8 ov490_ov10640_read(struct i2c_client *client, u16 addr) -+{ -+ u8 reg_val = 0; -+ -+ reg16_write(client, 0xFFFD, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x5000, 0x01); /* read operation */ -+ reg16_write(client, 0x5001, addr >> 8); -+ reg16_write(client, 0x5002, addr & 0xff); -+ reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x00C0, 0xc1); -+ reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(1000, 1500); /* wait 1 ms */ -+ reg16_read(client, 0x5000, ®_val); -+ -+ return reg_val; -+} -+ -+static void ov490_ov10640_write(struct i2c_client *client, u16 addr, u8 val) -+{ -+ reg16_write(client, 0xFFFD, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x5000, 0x00); /* write operation */ -+ reg16_write(client, 0x5001, addr >> 8); -+ reg16_write(client, 0x5002, addr & 0xff); -+ reg16_write(client, 0x5003, val); -+ reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x00C0, 0xc1); -+} -+ -+static int ov490_s_stream(struct v4l2_subdev *sd, int enable) ++static int ov495_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + -+static int ov490_get_fmt(struct v4l2_subdev *sd, ++static int ov495_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); ++ struct ov495_priv *priv = to_ov495(client); + + if (format->pad) + return -EINVAL; @@ -7766,7 +13330,7 @@ index 0000000..812f367 + return 0; +} + -+static int ov490_set_fmt(struct v4l2_subdev *sd, ++static int ov495_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ @@ -7782,7 +13346,7 @@ index 0000000..812f367 + return 0; +} + -+static int ov490_enum_mbus_code(struct v4l2_subdev *sd, ++static int ov495_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ @@ -7794,464 +13358,175 @@ index 0000000..812f367 + return 0; +} + -+static int ov490_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); -+ -+ memcpy(edid->edid, priv->id, 6); -+ -+ edid->edid[6] = 0xff; -+ edid->edid[7] = client->addr; -+ edid->edid[8] = OV490_VERSION_REG >> 8; -+ edid->edid[9] = OV490_VERSION_REG & 0xff; -+ -+ return 0; -+} -+ -+static int ov490_set_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct v4l2_rect *rect = &sel->r; -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); -+ -+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || -+ sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ rect->left = ALIGN(rect->left, 2); -+ rect->top = ALIGN(rect->top, 2); -+ rect->width = ALIGN(rect->width, 2); -+ rect->height = ALIGN(rect->height, 2); -+ -+ if ((rect->left + rect->width > priv->max_width) || -+ (rect->top + rect->height > priv->max_height)) -+ *rect = priv->rect; -+ -+ priv->rect.left = rect->left; -+ priv->rect.top = rect->top; -+ priv->rect.width = rect->width; -+ priv->rect.height = rect->height; -+ -+ return 0; -+} -+ -+static int ov490_get_selection(struct v4l2_subdev *sd, -+ struct v4l2_subdev_pad_config *cfg, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); -+ -+ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) -+ return -EINVAL; -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = priv->max_width; -+ sel->r.height = priv->max_height; -+ return 0; -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = priv->max_width; -+ sel->r.height = priv->max_height; -+ return 0; -+ case V4L2_SEL_TGT_CROP: -+ sel->r = priv->rect; -+ return 0; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int ov490_g_mbus_config(struct v4l2_subdev *sd, -+ struct v4l2_mbus_config *cfg) -+{ -+ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | -+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; -+ cfg->type = V4L2_MBUS_CSI2; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_VIDEO_ADV_DEBUG -+static int ov490_g_register(struct v4l2_subdev *sd, -+ struct v4l2_dbg_register *reg) ++static int ov495_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; -+ u8 val = 0; ++ struct ov495_priv *priv = to_ov495(client); + -+ ret = reg16_read(client, (u16)reg->reg, &val); -+ if (ret < 0) -+ return ret; ++ memcpy(edid->edid, priv->id, 6); + -+ reg->val = val; -+ reg->size = sizeof(u16); ++ edid->edid[6] = 0xff; ++ edid->edid[7] = client->addr; ++ edid->edid[8] = OV495_VERSION_REG >> 8; ++ edid->edid[9] = OV495_VERSION_REG & 0xff; + + return 0; +} + -+static int ov490_s_register(struct v4l2_subdev *sd, -+ const struct v4l2_dbg_register *reg) ++static int ov495_set_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) +{ ++ struct v4l2_rect *rect = &sel->r; + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; ++ struct ov495_priv *priv = to_ov495(client); + -+ ret = reg16_write(client, (u16)reg->reg, (u8)reg->val); -+ if ((u8)reg->reg == 0xFFFD) -+ usleep_range(100, 150); /* wait 100 us */ -+ if ((u8)reg->reg == 0xFFFE) -+ usleep_range(100, 150); /* wait 100 us */ -+ return ret; -+} -+#endif ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || ++ sel->target != V4L2_SEL_TGT_CROP) ++ return -EINVAL; + -+static struct v4l2_subdev_core_ops ov490_core_ops = { -+#ifdef CONFIG_VIDEO_ADV_DEBUG -+ .g_register = ov490_g_register, -+ .s_register = ov490_s_register, -+#endif -+}; ++ rect->left = ALIGN(rect->left, 2); ++ rect->top = ALIGN(rect->top, 2); ++ rect->width = ALIGN(rect->width, 2); ++ rect->height = ALIGN(rect->height, 2); + -+static int ov490_s_gamma(int a, int ref) -+{ -+ if ((a + ref) > 0xff) -+ return 0xff; ++ if ((rect->left + rect->width > priv->max_width) || ++ (rect->top + rect->height > priv->max_height)) ++ *rect = priv->rect; + -+ if ((a + ref) < 0) -+ return 0; ++ priv->rect.left = rect->left; ++ priv->rect.top = rect->top; ++ priv->rect.width = rect->width; ++ priv->rect.height = rect->height; + -+ return a + ref; ++ return 0; +} + -+static int ov490_s_ctrl(struct v4l2_ctrl *ctrl) ++static int ov495_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) +{ -+ struct v4l2_subdev *sd = to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); -+ int ret = -EINVAL; -+ -+ if (!priv->init_complete) -+ return 0; -+ -+ switch (ctrl->id) { -+ case V4L2_CID_BRIGHTNESS: -+ /* SDE (rough) brightness */ -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); -+ ret |= reg16_write(client, 0x5001, ctrl->val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xf1); -+ break; -+ case V4L2_CID_CONTRAST: -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xfd); -+ break; -+ case V4L2_CID_SATURATION: -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xf3); -+ break; -+ case V4L2_CID_HUE: -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xf5); -+ break; -+ case V4L2_CID_GAMMA: -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ov490_s_gamma(ctrl->val, 0x12)); -+ ret |= reg16_write(client, 0x5001, ov490_s_gamma(ctrl->val, 0x20)); -+ ret |= reg16_write(client, 0x5002, ov490_s_gamma(ctrl->val, 0x3b)); -+ ret |= reg16_write(client, 0x5003, ov490_s_gamma(ctrl->val, 0x5d)); -+ ret |= reg16_write(client, 0x5004, ov490_s_gamma(ctrl->val, 0x6a)); -+ ret |= reg16_write(client, 0x5005, ov490_s_gamma(ctrl->val, 0x76)); -+ ret |= reg16_write(client, 0x5006, ov490_s_gamma(ctrl->val, 0x81)); -+ ret |= reg16_write(client, 0x5007, ov490_s_gamma(ctrl->val, 0x8b)); -+ ret |= reg16_write(client, 0x5008, ov490_s_gamma(ctrl->val, 0x96)); -+ ret |= reg16_write(client, 0x5009, ov490_s_gamma(ctrl->val, 0x9e)); -+ ret |= reg16_write(client, 0x500a, ov490_s_gamma(ctrl->val, 0xae)); -+ ret |= reg16_write(client, 0x500b, ov490_s_gamma(ctrl->val, 0xbc)); -+ ret |= reg16_write(client, 0x500c, ov490_s_gamma(ctrl->val, 0xcf)); -+ ret |= reg16_write(client, 0x500d, ov490_s_gamma(ctrl->val, 0xde)); -+ ret |= reg16_write(client, 0x500e, ov490_s_gamma(ctrl->val, 0xec)); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xf9); -+ break; -+ case V4L2_CID_SHARPNESS: -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xfb); -+ break; -+ case V4L2_CID_AUTOGAIN: -+ case V4L2_CID_GAIN: -+ case V4L2_CID_EXPOSURE: -+ if (ctrl->id == V4L2_CID_AUTOGAIN) -+ priv->autogain = ctrl->val; -+ if (ctrl->id == V4L2_CID_GAIN) -+ priv->gain = ctrl->val; -+ if (ctrl->id == V4L2_CID_EXPOSURE) -+ priv->exposure = ctrl->val; ++ struct ov495_priv *priv = to_ov495(client); + -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, !priv->autogain); -+ ret |= reg16_write(client, 0x5001, priv->exposure >> 8); -+ ret |= reg16_write(client, 0x5002, priv->exposure & 0xff); -+ ret |= reg16_write(client, 0x5003, priv->exposure >> 8); -+ ret |= reg16_write(client, 0x5004, priv->exposure & 0xff); -+ ret |= reg16_write(client, 0x5005, priv->exposure >> 8); -+ ret |= reg16_write(client, 0x5006, priv->exposure & 0xff); -+ ret |= reg16_write(client, 0x5007, priv->gain >> 8); -+ ret |= reg16_write(client, 0x5008, priv->gain & 0xff); -+ ret |= reg16_write(client, 0x5009, priv->gain >> 8); -+ ret |= reg16_write(client, 0x500a, priv->gain & 0xff); -+ ret |= reg16_write(client, 0x500b, priv->gain >> 8); -+ ret |= reg16_write(client, 0x500c, priv->gain & 0xff); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xea); -+ break; -+ case V4L2_CID_AUTO_WHITE_BALANCE: -+ case V4L2_CID_RED_BALANCE: -+ case V4L2_CID_BLUE_BALANCE: -+ if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE) -+ priv->awb = ctrl->val; -+ if (ctrl->id == V4L2_CID_RED_BALANCE) { -+ priv->red = ctrl->val; -+ priv->red <<= 8; -+ priv->green_r = priv->red / 2; -+ } -+ if (ctrl->id == V4L2_CID_BLUE_BALANCE) { -+ priv->blue = ctrl->val; -+ priv->blue <<= 8; -+ priv->green_b = priv->blue / 2; -+ } ++ if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; + -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, !priv->awb); -+ ret |= reg16_write(client, 0x5001, priv->red >> 8); -+ ret |= reg16_write(client, 0x5002, priv->red & 0xff); -+ ret |= reg16_write(client, 0x5003, priv->green_r >> 8); -+ ret |= reg16_write(client, 0x5004, priv->green_r & 0xff); -+ ret |= reg16_write(client, 0x5005, priv->green_b >> 8); -+ ret |= reg16_write(client, 0x5006, priv->green_b & 0xff); -+ ret |= reg16_write(client, 0x5007, priv->blue >> 8); -+ ret |= reg16_write(client, 0x5008, priv->blue & 0xff); -+ ret |= reg16_write(client, 0x5009, priv->red >> 8); -+ ret |= reg16_write(client, 0x500a, priv->red & 0xff); -+ ret |= reg16_write(client, 0x500b, priv->green_r >> 8); -+ ret |= reg16_write(client, 0x500c, priv->green_r & 0xff); -+ ret |= reg16_write(client, 0x500d, priv->green_b >> 8); -+ ret |= reg16_write(client, 0x500e, priv->green_b & 0xff); -+ ret |= reg16_write(client, 0x500f, priv->blue >> 8); -+ ret |= reg16_write(client, 0x5010, priv->blue & 0xff); -+ ret |= reg16_write(client, 0x5011, priv->red >> 8); -+ ret |= reg16_write(client, 0x5012, priv->red & 0xff); -+ ret |= reg16_write(client, 0x5013, priv->green_r >> 8); -+ ret |= reg16_write(client, 0x5014, priv->green_r & 0xff); -+ ret |= reg16_write(client, 0x5015, priv->green_b >> 8); -+ ret |= reg16_write(client, 0x5016, priv->green_b & 0xff); -+ ret |= reg16_write(client, 0x5017, priv->blue >> 8); -+ ret |= reg16_write(client, 0x5018, priv->blue & 0xff); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xeb); -+ break; -+ case V4L2_CID_HFLIP: -+#if 1 -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0x5001, 0x00); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xdc); -+#else -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128 -+ ret |= reg16_write(client, 0x5001, 0x31); -+ ret |= reg16_write(client, 0x5002, 0x28); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 0); -+ val |= (ctrl->val << 0); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128 -+ ret |= reg16_write(client, 0x5001, 0x31); -+ ret |= reg16_write(client, 0x5002, 0x28); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = priv->max_width; ++ sel->r.height = priv->max_height; ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = priv->max_width; ++ sel->r.height = priv->max_height; ++ return 0; ++ case V4L2_SEL_TGT_CROP: ++ sel->r = priv->rect; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} + -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291 -+ ret |= reg16_write(client, 0x5001, 0x32); -+ ret |= reg16_write(client, 0x5002, 0x91); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 1); -+ val |= (ctrl->val << 1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291 -+ ret |= reg16_write(client, 0x5001, 0x32); -+ ret |= reg16_write(client, 0x5002, 0x91); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); ++static int ov495_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ cfg->type = V4L2_MBUS_CSI2; + -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090 -+ ret |= reg16_write(client, 0x5001, 0x30); -+ ret |= reg16_write(client, 0x5002, 0x90); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 2); -+ val |= (ctrl->val << 2); -+ ret |= reg16_write(client, 0xFFFE, 0x19); ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov495_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val = 0; ++ ++ ret = reg16_read(client, (u16)reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int ov495_s_register(struct v4l2_subdev *sd, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ ret = reg16_write(client, (u16)reg->reg, (u8)reg->val); ++ if ((u8)reg->reg == 0xFFFD) + usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090 -+ ret |= reg16_write(client, 0x5001, 0x30); -+ ret |= reg16_write(client, 0x5002, 0x90); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); ++ if ((u8)reg->reg == 0xFFFE) + usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); ++ return ret; ++} +#endif -+ break; -+ case V4L2_CID_VFLIP: -+#if 1 -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, ctrl->val); -+ ret |= reg16_write(client, 0x5001, 0x01); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xdc); -+#else -+ ret = reg16_write(client, 0xFFFD, 0x80); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3128 -+ ret |= reg16_write(client, 0x5001, 0x31); -+ ret |= reg16_write(client, 0x5002, 0x28); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 1); -+ val |= (ctrl->val << 1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3128 -+ ret |= reg16_write(client, 0x5001, 0x31); -+ ret |= reg16_write(client, 0x5002, 0x28); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); + -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3291 -+ ret |= reg16_write(client, 0x5001, 0x32); -+ ret |= reg16_write(client, 0x5002, 0x91); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 2); -+ val |= (ctrl->val << 2); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3291 -+ ret |= reg16_write(client, 0x5001, 0x32); -+ ret |= reg16_write(client, 0x5002, 0x91); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); ++static struct v4l2_subdev_core_ops ov495_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov495_g_register, ++ .s_register = ov495_s_register, ++#endif ++}; + -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x01); // read 0x3090 -+ ret |= reg16_write(client, 0x5001, 0x30); -+ ret |= reg16_write(client, 0x5002, 0x90); -+ ret |= reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+ ret |= reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_read(client, 0x5000, &val); -+ val &= ~(0x1 << 3); -+ val |= (ctrl->val << 3); -+ ret |= reg16_write(client, 0xFFFE, 0x19); ++static int ov495_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov495_priv *priv = to_ov495(client); ++ int ret = -EINVAL; ++ ++ if (!priv->init_complete) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ break; ++ case V4L2_CID_CONTRAST: ++ break; ++ case V4L2_CID_SATURATION: ++ break; ++ case V4L2_CID_HUE: ++ break; ++ case V4L2_CID_GAMMA: ++ break; ++ case V4L2_CID_SHARPNESS: ++ break; ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_GAIN: ++ case V4L2_CID_EXPOSURE: ++ break; ++ case V4L2_CID_HFLIP: ++ ret = reg16_write(client, 0x3516, 0x00); ++ ret |= reg16_write(client, 0x0ffc, 0x00); ++ ret |= reg16_write(client, 0x0500, ctrl->val); ++ ret |= reg16_write(client, 0x0501, 0x00); + usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x5000, 0x00); // write 0x3090 -+ ret |= reg16_write(client, 0x5001, 0x30); -+ ret |= reg16_write(client, 0x5002, 0x90); -+ ret |= reg16_write(client, 0x5003, val); -+ ret |= reg16_write(client, 0xFFFE, 0x80); ++ ret |= reg16_write(client, 0x30C0, 0xdc); ++ ret |= reg16_write(client, 0x3516, 0x01); ++ break; ++ case V4L2_CID_VFLIP: ++ ret = reg16_write(client, 0x3516, 0x00); ++ ret |= reg16_write(client, 0x0ffc, 0x00); ++ ret |= reg16_write(client, 0x0500, ctrl->val); ++ ret |= reg16_write(client, 0x0501, 0x01); + usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x00C0, 0xc1); -+#endif ++ ret |= reg16_write(client, 0x30C0, 0xdc); ++ ret |= reg16_write(client, 0x3516, 0x01); + break; + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + ret = 0; @@ -8261,198 +13536,153 @@ index 0000000..812f367 + return ret; +} + -+static const struct v4l2_ctrl_ops ov490_ctrl_ops = { -+ .s_ctrl = ov490_s_ctrl, ++static const struct v4l2_ctrl_ops ov495_ctrl_ops = { ++ .s_ctrl = ov495_s_ctrl, +}; + -+static struct v4l2_subdev_video_ops ov490_video_ops = { -+ .s_stream = ov490_s_stream, -+ .g_mbus_config = ov490_g_mbus_config, ++static struct v4l2_subdev_video_ops ov495_video_ops = { ++ .s_stream = ov495_s_stream, ++ .g_mbus_config = ov495_g_mbus_config, +}; + -+static const struct v4l2_subdev_pad_ops ov490_subdev_pad_ops = { -+ .get_edid = ov490_get_edid, -+ .enum_mbus_code = ov490_enum_mbus_code, -+ .get_selection = ov490_get_selection, -+ .set_selection = ov490_set_selection, -+ .get_fmt = ov490_get_fmt, -+ .set_fmt = ov490_set_fmt, ++static const struct v4l2_subdev_pad_ops ov495_subdev_pad_ops = { ++ .get_edid = ov495_get_edid, ++ .enum_mbus_code = ov495_enum_mbus_code, ++ .get_selection = ov495_get_selection, ++ .set_selection = ov495_set_selection, ++ .get_fmt = ov495_get_fmt, ++ .set_fmt = ov495_set_fmt, +}; + -+static struct v4l2_subdev_ops ov490_subdev_ops = { -+ .core = &ov490_core_ops, -+ .video = &ov490_video_ops, -+ .pad = &ov490_subdev_pad_ops, ++static struct v4l2_subdev_ops ov495_subdev_ops = { ++ .core = &ov495_core_ops, ++ .video = &ov495_video_ops, ++ .pad = &ov495_subdev_pad_ops, +}; + -+static void ov490_otp_id_read(struct i2c_client *client) ++static void ov495_otp_id_read(struct i2c_client *client) +{ -+ struct ov490_priv *priv = to_ov490(client); ++ struct ov495_priv *priv = to_ov495(client); + int i; -+ int otp_bank0_allzero = 1; + +#if 0 -+ /* read camera id from ov490 OTP memory */ ++ /* read camera id from ov495 OTP memory */ + reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x28); ++ reg16_write(client, 0xFFFE, 0x20); + usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0xE084, 0x40); /* manual mode, bank#0 */ -+ reg16_write(client, 0xE081, 1); /* start OTP read */ ++ reg16_write(client, 0x7384, 0x40); /* manual mode, bank#0 */ ++ reg16_write(client, 0x7381, 1); /* start OTP read */ + + usleep_range(25000, 26000); /* wait 25 ms */ + + for (i = 0; i < 6; i++) -+ reg16_read(client, 0xe000 + i + 4, &priv->id[i]); ++ reg16_read(client, 0x7300 + i + 4, &priv->id[i]); +#else -+ /* read camera id from ov10640 OTP memory */ -+ ov490_ov10640_write(client, 0x349C, 1); ++ /* read camera id from ov2775 OTP memory */ ++ reg16_write(client, 0x3516, 0x00); /* unlock write */ ++ reg16_write(client, 0x0FFC, 0); ++ reg16_write(client, 0x0500, 0x00); /* write 0x34a1 -> 1 */ ++ reg16_write(client, 0x0501, 0x34); ++ reg16_write(client, 0x0502, 0xa1); ++ reg16_write(client, 0x0503, 1); ++ reg16_write(client, 0x30C0, 0xc1); ++ + usleep_range(25000, 25500); /* wait 25 ms */ + + for (i = 0; i < 6; i++) { -+ /* first 6 bytes are equal on all ov10640 */ -+ priv->id[i] = ov490_ov10640_read(client, 0x349e + i + 6); -+ if (priv->id[i]) -+ otp_bank0_allzero = 0; -+ } -+ -+ if (otp_bank0_allzero) { -+ ov490_ov10640_write(client, 0x3495, 0x41); /* bank#1 */ -+ ov490_ov10640_write(client, 0x349C, 1); -+ usleep_range(25000, 25500); /* wait 25 ms */ -+ -+ for (i = 0; i < 6; i++) -+ priv->id[i] = ov490_ov10640_read(client, 0x34ae + i); ++ reg16_write(client, 0x3516, 0x00); /* unlock write */ ++ reg16_write(client, 0x0500, 0x01); /* read (0x7a00 + i) */ ++ reg16_write(client, 0x0501, 0x7a); ++ reg16_write(client, 0x0502, 0x00 + i + (i < 3 ? 11 : 3)); /* take bytes 11,12,13,6,7,8 */ ++ reg16_write(client, 0x30C0, 0xc1); ++ usleep_range(1000, 1500); /* wait 1 ms */ ++ reg16_read(client, 0x0500, &priv->id[i]); + } +#endif +} + -+static ssize_t ov490_otp_id_show(struct device *dev, ++static ssize_t ov495_otp_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov490_priv *priv = to_ov490(client); ++ struct ov495_priv *priv = to_ov495(client); + + return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", + priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); +} + -+static DEVICE_ATTR(otp_id_ov490, S_IRUGO, ov490_otp_id_show, NULL); ++static DEVICE_ATTR(otp_id_ov495, S_IRUGO, ov495_otp_id_show, NULL); + -+static int ov490_initialize(struct i2c_client *client) ++static int ov495_initialize(struct i2c_client *client) +{ -+ struct ov490_priv *priv = to_ov490(client); -+ u8 val = 0; ++ struct ov495_priv *priv = to_ov495(client); + u8 pid = 0, ver = 0; -+ int ret = 0, timeout, retry_timeout = 3; -+ -+ if (priv->is_fixed_sensor) { -+ dev_info(&client->dev, "ov490/ov10640 fixed-sensor res %dx%d\n", priv->max_width, priv->max_height); -+ return 0; -+ } -+ -+ ov490_s_port(client, 1); ++ int ret = 0; ++ int tmp_addr; + + /* check and show product ID and manufacturer ID */ + reg16_write(client, 0xFFFD, 0x80); + reg16_write(client, 0xFFFE, 0x80); + usleep_range(100, 150); /* wait 100 us */ -+ reg16_read(client, OV490_PID, &pid); -+ reg16_read(client, OV490_VER, &ver); ++ reg16_read(client, OV495_PID, &pid); ++ reg16_read(client, OV495_VER, &ver); + -+ if (OV490_VERSION(pid, ver) != OV490_VERSION_REG) { -+ dev_dbg(&client->dev, "Product ID error %x:%x\n", pid, ver); ++ if (OV495_VERSION(pid, ver) != OV495_VERSION_REG) { ++ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto err; + } + -+ if (unlikely(conf_link)) -+ goto out; -+ -+again: -+ /* Check if firmware booted by reading stream-on status */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x29); -+ usleep_range(100, 150); /* wait 100 us */ -+ for (timeout = 300; timeout > 0; timeout--) { -+ reg16_read(client, 0xd000, &val); -+ if (val == 0x0c) -+ break; -+ mdelay(1); -+ } -+ -+ /* wait firmware apps started by reading OV10640 ID */ -+ for (;timeout > 0; timeout--) { -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x5000, 0x01); -+ reg16_write(client, 0x5001, 0x30); -+ reg16_write(client, 0x5002, 0x0a); -+ reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0xC0, 0xc1); -+ reg16_write(client, 0xFFFE, 0x19); -+ usleep_range(1000, 1500); /* wait 1 ms */ -+ reg16_read(client, 0x5000, &val); -+ if (val == 0xa6) -+ break; -+ mdelay(1); ++ /* setup XCLK */ ++ tmp_addr = client->addr; ++ if (priv->ti9x4_addr) { ++ /* CLK_OUT=22.5792*160*M/N/CLKDIV -> CLK_OUT=25MHz: CLKDIV=4, M=7, N=253: 22.5792*160/4*7/253=24.989MHz=CLK_OUT */ ++ client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++ reg8_write(client, 0x06, 0x47); /* Set CLKDIV and M */ ++ reg8_write(client, 0x07, 0xfd); /* Set N */ + } ++ client->addr = tmp_addr; + -+ if (!timeout) { -+ dev_err(&client->dev, "Timeout firmware boot wait, retrying\n"); -+ /* reset OV10640 using RESETB pin controlled by OV490 GPIO0 */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x0050, 0x01); -+ reg16_write(client, 0x0054, 0x01); -+ reg16_write(client, 0x0058, 0x00); -+ mdelay(10); -+ reg16_write(client, 0x0058, 0x01); -+ /* reset OV490 using RESETB pin controlled by serializer */ -+ ov490_reset(client); -+ if (retry_timeout--) -+ goto again; -+ } ++ if (unlikely(force_conf_link)) ++ goto out; + ++#if 0 + /* read resolution used by current firmware */ + reg16_write(client, 0xFFFD, 0x80); + reg16_write(client, 0xFFFE, 0x82); + usleep_range(100, 150); /* wait 100 us */ -+ reg16_read(client, OV490_ISP_HSIZE_HIGH, &val); ++ reg16_read(client, OV495_ISP_HSIZE_HIGH, &val); + priv->max_width = val; -+ reg16_read(client, OV490_ISP_HSIZE_LOW, &val); ++ reg16_read(client, OV495_ISP_HSIZE_LOW, &val); + priv->max_width = (priv->max_width << 8) | val; -+ reg16_read(client, OV490_ISP_VSIZE_HIGH, &val); ++ reg16_read(client, OV495_ISP_VSIZE_HIGH, &val); + priv->max_height = val; -+ reg16_read(client, OV490_ISP_VSIZE_LOW, &val); ++ reg16_read(client, OV495_ISP_VSIZE_LOW, &val); + priv->max_height = (priv->max_height << 8) | val; ++#else ++ priv->max_width = 1920; ++ priv->max_height = 1080; ++#endif ++ ++ /* set virtual channel */ ++ ov495_regs_wizard[3].val = 0x1e | (priv->port << 6); + /* Program wizard registers */ -+ ov490_set_regs(client, ov490_regs_wizard, ARRAY_SIZE(ov490_regs_wizard)); -+ /* Set DVP bit swap */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x28); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x6009, priv->dvp_order << 4); ++ ov495_set_regs(client, ov495_regs_wizard, ARRAY_SIZE(ov495_regs_wizard)); + /* Read OTP IDs */ -+ ov490_otp_id_read(client); ++ ov495_otp_id_read(client); + +out: -+ dev_info(&client->dev, "ov490/ov10640 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ dev_info(&client->dev, "ov495/ov2775 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", + pid, ver, priv->max_width, priv->max_height, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); +err: -+ ov490_s_port(client, 0); -+ + return ret; +} + -+static int ov490_parse_dt(struct device_node *np, struct ov490_priv *priv) ++static int ov495_parse_dt(struct device_node *np, struct ov495_priv *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); + int i; -+ const char *fixed_sensor; + struct device_node *endpoint = NULL, *rendpoint = NULL; + int tmp_addr = 0; + @@ -8463,26 +13693,10 @@ index 0000000..812f367 + + of_node_put(endpoint); + -+ of_property_read_u32(endpoint, "dvp-order", &priv->dvp_order); -+ + rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); + if (!rendpoint) + continue; + -+ if (!of_property_read_u32(rendpoint, "max9271-addr", &priv->max9271_addr) && -+ !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->max9286_addr) && -+ !kstrtouint(strrchr(rendpoint->full_name, '@') + 1, 0, &priv->port)) { -+ if (of_property_read_u32(rendpoint->parent->parent, "maxim,resetb-gpio", &priv->gpio_resetb)) { -+ priv->gpio_resetb = -1; -+ } else { -+ if (of_property_read_bool(rendpoint->parent->parent, "maxim,resetb-active-high")) -+ priv->active_low_resetb = false; -+ else -+ priv->active_low_resetb = true; -+ } -+ break; -+ } -+ + if (!of_property_read_u32(rendpoint, "ti9x3-addr", &priv->ti9x3_addr) && + !of_property_match_string(rendpoint->parent->parent, "compatible", "ti,ti9x4") && + !of_property_read_u32(rendpoint->parent->parent, "reg", &priv->ti9x4_addr) && @@ -8490,61 +13704,34 @@ index 0000000..812f367 + break; + } + -+ if (!priv->max9286_addr && !priv->ti9x4_addr) { -+ dev_err(&client->dev, "deserializer does not present for OV490\n"); ++ if (!priv->ti9x4_addr) { ++ dev_err(&client->dev, "deserializer does not present for OV495\n"); + return -EINVAL; + } + -+ ov490_s_port(client, 1); -+ + /* setup I2C translator address */ + tmp_addr = client->addr; -+ if (priv->max9286_addr) { -+ client->addr = priv->max9271_addr; /* Serializer I2C address */ -+ -+ reg8_write(client, 0x09, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x0A, OV490_I2C_ADDR << 1); /* Sensor native I2C address */ -+ usleep_range(2000, 2500); /* wait 2ms */ -+ }; + if (priv->ti9x4_addr) { + client->addr = priv->ti9x4_addr; /* Deserializer I2C address */ + + reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ + usleep_range(2000, 2500); /* wait 2ms */ + reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x5d, OV490_I2C_ADDR << 1); /* Sensor native I2C address */ ++ reg8_write(client, 0x5d, OV495_I2C_ADDR << 1); /* Sensor native I2C address */ + + reg8_write(client, 0x6e, 0x9a); /* GPIO0 - fsin, GPIO1 - resetb */ ++ /* TODO: why too long? move logic to workqueue? */ ++ mdelay(350); /* time needed to boot all sensor IPs */ + } + client->addr = tmp_addr; + -+ if (!of_property_read_string(np, "maxim,fixed-sensor", &fixed_sensor) && -+ strcmp(fixed_sensor, "ov490") == 0) { -+ if (of_property_read_u32(np, "maxim,width", &priv->max_width)) -+ priv->max_width = 1280; -+ -+ if (of_property_read_u32(np, "maxim,height", &priv->max_height)) -+ priv->max_height = 966; -+ -+ priv->is_fixed_sensor = true; -+ } -+ -+ /* module params override dts */ -+ if (dvp_order) -+ priv->dvp_order = dvp_order; -+ if (max_width && max_height) { -+ priv->max_width = max_width; -+ priv->max_height = max_height; -+ priv->is_fixed_sensor = true; -+ } -+ + return 0; +} + -+static int ov490_probe(struct i2c_client *client, ++static int ov495_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ -+ struct ov490_priv *priv; ++ struct ov495_priv *priv; + struct v4l2_ctrl *ctrl; + int ret; + @@ -8552,47 +13739,36 @@ index 0000000..812f367 + if (!priv) + return -ENOMEM; + -+ v4l2_i2c_subdev_init(&priv->sd, client, &ov490_subdev_ops); ++ v4l2_i2c_subdev_init(&priv->sd, client, &ov495_subdev_ops); + priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + priv->exposure = 0x100; + priv->gain = 0x100; + priv->autogain = 1; -+ priv->red = 0x400; -+ priv->blue = 0x400; -+ priv->green_r = priv->red / 2; -+ priv->green_b = priv->blue / 2; -+ priv->awb = 1; + v4l2_ctrl_handler_init(&priv->hdl, 4); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 16, 1, 7); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_CONTRAST, 0, 16, 1, 7); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_SATURATION, 0, 7, 1, 2); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_HUE, 0, 23, 1, 12); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_GAMMA, -128, 128, 1, 0); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 10, 1, 3); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, -+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, priv->autogain); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, -+ V4L2_CID_RED_BALANCE, 2, 0xf, 1, priv->red >> 8); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, -+ V4L2_CID_BLUE_BALANCE, 2, 0xf, 1, priv->blue >> 8); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, -+ V4L2_CID_HFLIP, 0, 1, 1, 1); -+ v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); -+ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov490_ctrl_ops, ++ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); + if (ctrl) + ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; @@ -8610,11 +13786,11 @@ index 0000000..812f367 + if (ret < 0) + goto cleanup; + -+ ret = ov490_parse_dt(client->dev.of_node, priv); ++ ret = ov495_parse_dt(client->dev.of_node, priv); + if (ret) + goto cleanup; + -+ ret = ov490_initialize(client); ++ ret = ov495_initialize(client); + if (ret < 0) + goto cleanup; + @@ -8627,7 +13803,7 @@ index 0000000..812f367 + if (ret) + goto cleanup; + -+ if (device_create_file(&client->dev, &dev_attr_otp_id_ov490) != 0) { ++ if (device_create_file(&client->dev, &dev_attr_otp_id_ov495) != 0) { + dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); + goto cleanup; + } @@ -8640,18 +13816,18 @@ index 0000000..812f367 + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->hdl); + v4l2_device_unregister_subdev(&priv->sd); -+#ifdef CONFIG_SOC_CAMERA_OV490_OV10640 ++#ifdef CONFIG_SOC_CAMERA_OV495_OV2775 + v4l_err(client, "failed to probe @ 0x%02x (%s)\n", + client->addr, client->adapter->name); +#endif + return ret; +} + -+static int ov490_remove(struct i2c_client *client) ++static int ov495_remove(struct i2c_client *client) +{ -+ struct ov490_priv *priv = i2c_get_clientdata(client); ++ struct ov495_priv *priv = i2c_get_clientdata(client); + -+ device_remove_file(&client->dev, &dev_attr_otp_id_ov490); ++ device_remove_file(&client->dev, &dev_attr_otp_id_ov495); + v4l2_async_unregister_subdev(&priv->sd); + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->hdl); @@ -8660,45 +13836,45 @@ index 0000000..812f367 + return 0; +} + -+#ifdef CONFIG_SOC_CAMERA_OV490_OV10640 -+static const struct i2c_device_id ov490_id[] = { -+ { "ov490-ov10640", 0 }, ++#ifdef CONFIG_SOC_CAMERA_OV495_OV2775 ++static const struct i2c_device_id ov495_id[] = { ++ { "ov495-ov2775", 0 }, + { } +}; -+MODULE_DEVICE_TABLE(i2c, ov490_id); ++MODULE_DEVICE_TABLE(i2c, ov495_id); + -+static const struct of_device_id ov490_of_ids[] = { -+ { .compatible = "ovti,ov490-ov10640", }, ++static const struct of_device_id ov495_of_ids[] = { ++ { .compatible = "ovti,ov495-ov2775", }, + { } +}; -+MODULE_DEVICE_TABLE(of, ov490_of_ids); ++MODULE_DEVICE_TABLE(of, ov495_of_ids); + -+static struct i2c_driver ov490_i2c_driver = { ++static struct i2c_driver ov495_i2c_driver = { + .driver = { -+ .name = "ov490-ov10640", -+ .of_match_table = ov490_of_ids, ++ .name = "ov495-ov2775", ++ .of_match_table = ov495_of_ids, + }, -+ .probe = ov490_probe, -+ .remove = ov490_remove, -+ .id_table = ov490_id, ++ .probe = ov495_probe, ++ .remove = ov495_remove, ++ .id_table = ov495_id, +}; + -+module_i2c_driver(ov490_i2c_driver); ++module_i2c_driver(ov495_i2c_driver); + -+MODULE_DESCRIPTION("SoC Camera driver for OV490-OV10640"); ++MODULE_DESCRIPTION("SoC Camera driver for OV495-OV2775"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); +#endif -diff --git a/drivers/media/i2c/soc_camera/ov490_ov10640.h b/drivers/media/i2c/soc_camera/ov490_ov10640.h +diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.h b/drivers/media/i2c/soc_camera/ov495_ov2775.h new file mode 100644 -index 0000000..b22e93e +index 0000000..17c94ae --- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov490_ov10640.h -@@ -0,0 +1,102 @@ ++++ b/drivers/media/i2c/soc_camera/ov495_ov2775.h +@@ -0,0 +1,23 @@ +/* -+ * OmniVision ov490-ov10640 sensor camera wizard 1280x1080@30/UYVY/BT601/8bit ++ * OmniVision ov495-ov2775 sensor camera wizard 1920x1080@30/UYVY/MIPI + * -+ * Copyright (C) 2016-2017 Cogent Embedded, Inc. ++ * Copyright (C) 2017 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -8706,107 +13882,28 @@ index 0000000..b22e93e + * option) any later version. + */ + -+//#define OV490_DISPLAY_PATTERN -+ -+struct ov490_reg { ++struct ov495_reg { + u16 reg; + u8 val; +}; + -+static const struct ov490_reg ov490_regs_wizard[] = { -+/* The following registers should match firmware */ -+{0xfffd, 0x80}, -+{0xfffe, 0x82}, -+{0x0071, 0x11}, -+{0x0075, 0x11}, -+{0xfffe, 0x29}, -+{0x6010, 0x01}, -+/* ov490 EMB line disable in YUV and RAW data, NOTE: EMB line is still used in ISP and sensor */ -+{0xe000, 0x14}, -+#if 0 /* do not disable EMB line in ISP! */ -+{0x4017, 0x00}, -+#endif -+{0xfffe, 0x28}, -+{0x6000, 0x04}, -+{0x6004, 0x00}, -+{0x6008, 0x00}, // PCLK polarity - useless due to silicon bug -> use 0x808000bb register -+{0xfffe, 0x80}, -+{0x0091, 0x00}, -+{0x00bb, 0x1d}, // bit[3]=0 - PCLK polarity workaround -+/* ov10640 EMB line disable */ -+#if 0 /* do not disable EMB line in sensor! */ -+{0xfffe, 0x19}, -+{0x5000, 0x00}, -+{0x5001, 0x30}, -+{0x5002, 0x91}, -+{0x5003, 0x08}, -+{0xfffe, 0x80}, -+{0x00c0, 0xc1}, -+#endif -+/* Ov490 FSIN: app_fsin_from_fsync */ -+{0xfffe, 0x85}, -+{0x0008, 0x00}, -+{0x0009, 0x01}, -+{0x000A, 0x05}, // fsin0 src -+{0x000B, 0x00}, -+{0x0030, 0x02}, // fsin0_delay -+{0x0031, 0x00}, -+{0x0032, 0x00}, -+{0x0033, 0x00}, -+{0x0038, 0x02}, // fsin1_delay -+{0x0039, 0x00}, -+{0x003A, 0x00}, -+{0x003B, 0x00}, -+{0x0070, 0x2C}, // fsin0_length -+{0x0071, 0x01}, -+{0x0072, 0x00}, -+{0x0073, 0x00}, -+{0x0074, 0x64}, // fsin1_length -+{0x0075, 0x00}, -+{0x0076, 0x00}, -+{0x0077, 0x00}, -+{0x0000, 0x14}, -+{0x0001, 0x00}, -+{0x0002, 0x00}, -+{0x0003, 0x00}, -+{0x0004, 0x32}, // load fsin0,load fsin1,load other, it will be cleared automatically. -+{0x0005, 0x00}, -+{0x0006, 0x00}, -+{0x0007, 0x00}, -+{0xfffe, 0x80}, -+{0x0081, 0x00}, // 03;SENSOR FSIN -+/* ov10640 FSIN */ -+{0xfffe, 0x19}, -+{0x5000, 0x00}, -+{0x5001, 0x30}, -+{0x5002, 0x8c}, -+{0x5003, 0xb2}, -+{0xfffe, 0x80}, -+{0x00c0, 0xc1}, -+/* ov10640 HFLIP=1 by default */ -+{0xfffe, 0x19}, -+{0x5000, 0x01}, -+{0x5001, 0x00}, -+{0xfffe, 0x80}, -+{0x00c0, 0xdc}, -+#ifdef OV490_DISPLAY_PATTERN -+{0xfffd, 0x80}, -+{0xfffe, 0x19}, -+{0x5000, 0x02}, -+{0xfffe, 0x80}, -+{0x00c0, 0xd6}, -+#endif ++static struct ov495_reg ov495_regs_wizard[] = { ++{0x3516, 0x00}, /* unlock write */ ++{0xFFFD, 0x80}, ++{0xFFFE, 0x20}, ++{0x8017, 0x1e | (0 << 6)}, ++{0x7c10, 0x01}, /* UYVY */ +}; -diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.c b/drivers/media/i2c/soc_camera/ov495_ov2775.c +diff --git a/drivers/media/i2c/soc_camera/ox03a.c b/drivers/media/i2c/soc_camera/ox03a.c new file mode 100644 -index 0000000..e53c482 +index 0000000..d51512b9 --- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov495_ov2775.c -@@ -0,0 +1,639 @@ ++++ b/drivers/media/i2c/soc_camera/ox03a.c +@@ -0,0 +1,526 @@ +/* -+ * OmniVision ov495-ov2775 sensor camera driver ++ * OmniVision OX03A sensor camera driver + * -+ * Copyright (C) 2017 Cogent Embedded, Inc. ++ * Copyright (C) 2018 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -8825,106 +13922,88 @@ index 0000000..e53c482 +#include +#include + -+#include "ov495_ov2775.h" ++#include "ox03a.h" + -+#define OV495_I2C_ADDR 0x24 ++#define OX03A_I2C_ADDR 0x36 + -+#define OV495_PID 0x300a -+#define OV495_VER 0x300b -+#define OV495_VERSION_REG 0x0495 -+#define OV495_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff)) ++#define OX03A_PID 0x300A ++#define OX03A_VER 0x300B ++#define OX03A_VERSION_REG 0x5803 + -+#define OV495_ISP_HSIZE_LOW 0x60 -+#define OV495_ISP_HSIZE_HIGH 0x61 -+#define OV495_ISP_VSIZE_LOW 0x62 -+#define OV495_ISP_VSIZE_HIGH 0x63 ++#define OX03A_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR12_1X12 + -+struct ov495_priv { ++struct ox03a_priv { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct media_pad pad; + struct v4l2_rect rect; -+ int max_width; -+ int max_height; + int init_complete; + u8 id[6]; + int exposure; + int gain; + int autogain; + /* serializers */ -+ int max9286_addr; -+ int max9271_addr; + int ti9x4_addr; + int ti9x3_addr; + int port; + int gpio_resetb; + int gpio_fsin; -+ +}; + -+static int force_conf_link; -+ -+static __init int ov495_force_conf_link(char *str) -+{ -+ /* force configuration link */ -+ /* used only if robust firmware flashing required (f.e. recovery) */ -+ force_conf_link = 1; -+ return 0; -+} -+early_param("force_conf_link", ov495_force_conf_link); -+ -+static inline struct ov495_priv *to_ov495(const struct i2c_client *client) ++static inline struct ox03a_priv *to_ox03a(const struct i2c_client *client) +{ -+ return container_of(i2c_get_clientdata(client), struct ov495_priv, sd); ++ return container_of(i2c_get_clientdata(client), struct ox03a_priv, sd); +} + -+static int ov495_set_regs(struct i2c_client *client, -+ const struct ov495_reg *regs, int nr_regs) ++static int ox03a_set_regs(struct i2c_client *client, ++ const struct ox03a_reg *regs, int nr_regs) +{ + int i; + + for (i = 0; i < nr_regs; i++) { -+ if (reg16_write(client, regs[i].reg, regs[i].val)) { -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, regs[i].reg, regs[i].val); ++ if (regs[i].reg == OX03A_DELAY) { ++ mdelay(regs[i].val); ++ continue; + } ++ ++ reg16_write(client, regs[i].reg, regs[i].val); + } + + return 0; +} + -+static int ov495_s_stream(struct v4l2_subdev *sd, int enable) ++static int ox03a_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + -+static int ov495_get_fmt(struct v4l2_subdev *sd, ++static int ox03a_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + + if (format->pad) + return -EINVAL; + + mf->width = priv->rect.width; + mf->height = priv->rect.height; -+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->code = OX03A_MEDIA_BUS_FMT; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->field = V4L2_FIELD_NONE; + + return 0; +} + -+static int ov495_set_fmt(struct v4l2_subdev *sd, ++static int ox03a_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + -+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ mf->code = OX03A_MEDIA_BUS_FMT; + mf->colorspace = V4L2_COLORSPACE_SMPTE170M; + mf->field = V4L2_FIELD_NONE; + @@ -8934,40 +14013,40 @@ index 0000000..e53c482 + return 0; +} + -+static int ov495_enum_mbus_code(struct v4l2_subdev *sd, ++static int ox03a_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + -+ code->code = MEDIA_BUS_FMT_YUYV8_2X8; ++ code->code = OX03A_MEDIA_BUS_FMT; + + return 0; +} + -+static int ov495_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) ++static int ox03a_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + + memcpy(edid->edid, priv->id, 6); + + edid->edid[6] = 0xff; + edid->edid[7] = client->addr; -+ edid->edid[8] = OV495_VERSION_REG >> 8; -+ edid->edid[9] = OV495_VERSION_REG & 0xff; ++ edid->edid[8] = OX03A_VERSION_REG >> 8; ++ edid->edid[9] = OX03A_VERSION_REG & 0xff; + + return 0; +} + -+static int ov495_set_selection(struct v4l2_subdev *sd, ++static int ox03a_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_rect *rect = &sel->r; + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) @@ -8978,8 +14057,8 @@ index 0000000..e53c482 + rect->width = ALIGN(rect->width, 2); + rect->height = ALIGN(rect->height, 2); + -+ if ((rect->left + rect->width > priv->max_width) || -+ (rect->top + rect->height > priv->max_height)) ++ if ((rect->left + rect->width > OX03A_MAX_WIDTH) || ++ (rect->top + rect->height > OX03A_MAX_HEIGHT)) + *rect = priv->rect; + + priv->rect.left = rect->left; @@ -8990,12 +14069,12 @@ index 0000000..e53c482 + return 0; +} + -+static int ov495_get_selection(struct v4l2_subdev *sd, ++static int ox03a_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; @@ -9004,14 +14083,14 @@ index 0000000..e53c482 + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; -+ sel->r.width = priv->max_width; -+ sel->r.height = priv->max_height; ++ sel->r.width = OX03A_MAX_WIDTH; ++ sel->r.height = OX03A_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; -+ sel->r.width = priv->max_width; -+ sel->r.height = priv->max_height; ++ sel->r.width = OX03A_MAX_WIDTH; ++ sel->r.height = OX03A_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = priv->rect; @@ -9021,7 +14100,7 @@ index 0000000..e53c482 + } +} + -+static int ov495_g_mbus_config(struct v4l2_subdev *sd, ++static int ox03a_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | @@ -9032,7 +14111,7 @@ index 0000000..e53c482 +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG -+static int ov495_g_register(struct v4l2_subdev *sd, ++static int ox03a_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -9044,38 +14123,32 @@ index 0000000..e53c482 + return ret; + + reg->val = val; -+ reg->size = sizeof(u16); ++ reg->size = sizeof(u8); + + return 0; +} + -+static int ov495_s_register(struct v4l2_subdev *sd, ++static int ox03a_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ int ret; + -+ ret = reg16_write(client, (u16)reg->reg, (u8)reg->val); -+ if ((u8)reg->reg == 0xFFFD) -+ usleep_range(100, 150); /* wait 100 us */ -+ if ((u8)reg->reg == 0xFFFE) -+ usleep_range(100, 150); /* wait 100 us */ -+ return ret; ++ return reg16_write(client, (u16)reg->reg, (u8)reg->val); +} +#endif + -+static struct v4l2_subdev_core_ops ov495_core_ops = { ++static struct v4l2_subdev_core_ops ox03a_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG -+ .g_register = ov495_g_register, -+ .s_register = ov495_s_register, ++ .g_register = ox03a_g_register, ++ .s_register = ox03a_s_register, +#endif +}; + -+static int ov495_s_ctrl(struct v4l2_ctrl *ctrl) ++static int ox03a_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + int ret = -EINVAL; + + if (!priv->init_complete) @@ -9083,180 +14156,94 @@ index 0000000..e53c482 + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: -+ break; + case V4L2_CID_CONTRAST: -+ break; + case V4L2_CID_SATURATION: -+ break; + case V4L2_CID_HUE: -+ break; + case V4L2_CID_GAMMA: -+ break; + case V4L2_CID_SHARPNESS: -+ break; + case V4L2_CID_AUTOGAIN: + case V4L2_CID_GAIN: -+ case V4L2_CID_EXPOSURE: -+ break; -+ case V4L2_CID_HFLIP: -+ ret = reg16_write(client, 0x3516, 0x00); -+ ret |= reg16_write(client, 0x0ffc, 0x00); -+ ret |= reg16_write(client, 0x0500, ctrl->val); -+ ret |= reg16_write(client, 0x0501, 0x00); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x30C0, 0xdc); -+ ret |= reg16_write(client, 0x3516, 0x01); -+ break; -+ case V4L2_CID_VFLIP: -+ ret = reg16_write(client, 0x3516, 0x00); -+ ret |= reg16_write(client, 0x0ffc, 0x00); -+ ret |= reg16_write(client, 0x0500, ctrl->val); -+ ret |= reg16_write(client, 0x0501, 0x01); -+ usleep_range(100, 150); /* wait 100 us */ -+ ret |= reg16_write(client, 0x30C0, 0xdc); -+ ret |= reg16_write(client, 0x3516, 0x01); -+ break; -+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: -+ ret = 0; ++ case V4L2_CID_EXPOSURE: ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: + break; + } + + return ret; +} + -+static const struct v4l2_ctrl_ops ov495_ctrl_ops = { -+ .s_ctrl = ov495_s_ctrl, ++static const struct v4l2_ctrl_ops ox03a_ctrl_ops = { ++ .s_ctrl = ox03a_s_ctrl, +}; + -+static struct v4l2_subdev_video_ops ov495_video_ops = { -+ .s_stream = ov495_s_stream, -+ .g_mbus_config = ov495_g_mbus_config, ++static struct v4l2_subdev_video_ops ox03a_video_ops = { ++ .s_stream = ox03a_s_stream, ++ .g_mbus_config = ox03a_g_mbus_config, +}; + -+static const struct v4l2_subdev_pad_ops ov495_subdev_pad_ops = { -+ .get_edid = ov495_get_edid, -+ .enum_mbus_code = ov495_enum_mbus_code, -+ .get_selection = ov495_get_selection, -+ .set_selection = ov495_set_selection, -+ .get_fmt = ov495_get_fmt, -+ .set_fmt = ov495_set_fmt, ++static const struct v4l2_subdev_pad_ops ox03a_subdev_pad_ops = { ++ .get_edid = ox03a_get_edid, ++ .enum_mbus_code = ox03a_enum_mbus_code, ++ .get_selection = ox03a_get_selection, ++ .set_selection = ox03a_set_selection, ++ .get_fmt = ox03a_get_fmt, ++ .set_fmt = ox03a_set_fmt, +}; + -+static struct v4l2_subdev_ops ov495_subdev_ops = { -+ .core = &ov495_core_ops, -+ .video = &ov495_video_ops, -+ .pad = &ov495_subdev_pad_ops, ++static struct v4l2_subdev_ops ox03a_subdev_ops = { ++ .core = &ox03a_core_ops, ++ .video = &ox03a_video_ops, ++ .pad = &ox03a_subdev_pad_ops, +}; + -+static void ov495_otp_id_read(struct i2c_client *client) ++static void ox03a_otp_id_read(struct i2c_client *client) +{ -+ struct ov495_priv *priv = to_ov495(client); -+ int i; -+ -+#if 0 -+ /* read camera id from ov495 OTP memory */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x20); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_write(client, 0x7384, 0x40); /* manual mode, bank#0 */ -+ reg16_write(client, 0x7381, 1); /* start OTP read */ -+ -+ usleep_range(25000, 26000); /* wait 25 ms */ -+ -+ for (i = 0; i < 6; i++) -+ reg16_read(client, 0x7300 + i + 4, &priv->id[i]); -+#else -+ /* read camera id from ov2775 OTP memory */ -+ reg16_write(client, 0x3516, 0x00); /* unlock write */ -+ reg16_write(client, 0x0FFC, 0); -+ reg16_write(client, 0x0500, 0x00); /* write 0x34a1 -> 1 */ -+ reg16_write(client, 0x0501, 0x34); -+ reg16_write(client, 0x0502, 0xa1); -+ reg16_write(client, 0x0503, 1); -+ reg16_write(client, 0x30C0, 0xc1); -+ -+ usleep_range(25000, 25500); /* wait 25 ms */ -+ -+ for (i = 0; i < 6; i++) { -+ reg16_write(client, 0x3516, 0x00); /* unlock write */ -+ reg16_write(client, 0x0500, 0x01); /* read (0x7a00 + i) */ -+ reg16_write(client, 0x0501, 0x7a); -+ reg16_write(client, 0x0502, 0x00 + i + (i < 3 ? 11 : 3)); /* take bytes 11,12,13,6,7,8 */ -+ reg16_write(client, 0x30C0, 0xc1); -+ usleep_range(1000, 1500); /* wait 1 ms */ -+ reg16_read(client, 0x0500, &priv->id[i]); -+ } -+#endif +} + -+static ssize_t ov495_otp_id_show(struct device *dev, ++static ssize_t ox03a_otp_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct ov495_priv *priv = to_ov495(client); ++ struct ox03a_priv *priv = to_ox03a(client); + + return snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x\n", + priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); +} + -+static DEVICE_ATTR(otp_id_ov495, S_IRUGO, ov495_otp_id_show, NULL); ++static DEVICE_ATTR(otp_id_ox03a, S_IRUGO, ox03a_otp_id_show, NULL); + -+static int ov495_initialize(struct i2c_client *client) ++static int ox03a_initialize(struct i2c_client *client) +{ -+ struct ov495_priv *priv = to_ov495(client); -+ u8 pid = 0, ver = 0; ++ struct ox03a_priv *priv = to_ox03a(client); ++ u8 val = 0; ++ u16 pid; + int ret = 0; + -+ /* check and show product ID and manufacturer ID */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x80); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_read(client, OV495_PID, &pid); -+ reg16_read(client, OV495_VER, &ver); ++ /* check and show model ID */ ++ reg16_read(client, OX03A_PID, &val); ++ pid = val; ++ reg16_read(client, OX03A_VER, &val); ++ pid = (pid << 8) | val; + -+ if (OV495_VERSION(pid, ver) != OV495_VERSION_REG) { -+ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); ++ if (pid != OX03A_VERSION_REG) { ++ dev_dbg(&client->dev, "Product ID error %x\n", pid); + ret = -ENODEV; + goto err; + } + -+ if (unlikely(force_conf_link)) -+ goto out; -+ -+#if 0 -+ /* read resolution used by current firmware */ -+ reg16_write(client, 0xFFFD, 0x80); -+ reg16_write(client, 0xFFFE, 0x82); -+ usleep_range(100, 150); /* wait 100 us */ -+ reg16_read(client, OV495_ISP_HSIZE_HIGH, &val); -+ priv->max_width = val; -+ reg16_read(client, OV495_ISP_HSIZE_LOW, &val); -+ priv->max_width = (priv->max_width << 8) | val; -+ reg16_read(client, OV495_ISP_VSIZE_HIGH, &val); -+ priv->max_height = val; -+ reg16_read(client, OV495_ISP_VSIZE_LOW, &val); -+ priv->max_height = (priv->max_height << 8) | val; -+#else -+ priv->max_width = 1920; -+ priv->max_height = 1080; -+#endif -+ -+ /* set virtual channel */ -+ ov495_regs_wizard[3].val = 0x1e | (priv->port << 6); + /* Program wizard registers */ -+ ov495_set_regs(client, ov495_regs_wizard, ARRAY_SIZE(ov495_regs_wizard)); ++ ox03a_set_regs(client, ox03a_regs_wizard, ARRAY_SIZE(ox03a_regs_wizard)); + /* Read OTP IDs */ -+ ov495_otp_id_read(client); ++ ox03a_otp_id_read(client); + -+out: -+ dev_info(&client->dev, "ov495/ov2775 PID %x%x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", -+ pid, ver, priv->max_width, priv->max_height, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); ++ dev_info(&client->dev, "ox03a PID %x, res %dx%d, OTP_ID %02x:%02x:%02x:%02x:%02x:%02x\n", ++ pid, OX03A_MAX_WIDTH, OX03A_MAX_HEIGHT, priv->id[0], priv->id[1], priv->id[2], priv->id[3], priv->id[4], priv->id[5]); +err: + return ret; +} + -+static int ov495_parse_dt(struct device_node *np, struct ov495_priv *priv) ++static int ox03a_parse_dt(struct device_node *np, struct ox03a_priv *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); + int i; @@ -9282,7 +14269,7 @@ index 0000000..e53c482 + } + + if (!priv->ti9x4_addr) { -+ dev_err(&client->dev, "deserializer does not present for OV495\n"); ++ dev_err(&client->dev, "deserializer does not present\n"); + return -EINVAL; + } + @@ -9290,65 +14277,62 @@ index 0000000..e53c482 + tmp_addr = client->addr; + if (priv->ti9x4_addr) { + client->addr = priv->ti9x4_addr; /* Deserializer I2C address */ -+ + reg8_write(client, 0x4c, (priv->port << 4) | (1 << priv->port)); /* Select RX port number */ + usleep_range(2000, 2500); /* wait 2ms */ + reg8_write(client, 0x65, tmp_addr << 1); /* Sensor translated I2C address */ -+ reg8_write(client, 0x5d, OV495_I2C_ADDR << 1); /* Sensor native I2C address */ ++ reg8_write(client, 0x5d, OX03A_I2C_ADDR << 1); /* Sensor native I2C address */ ++// reg8_write(client, 0x6e, 0xa9); /* GPIO0 - reset, GPIO1 - fsin */ + -+ reg8_write(client, 0x6e, 0x9a); /* GPIO0 - fsin, GPIO1 - resetb */ -+ /* TODO: why too long? move logic to workqueue? */ -+ mdelay(350); /* time needed to boot all sensor IPs */ ++// client->addr = priv->ti9x3_addr; /* Serializer I2C address */ ++// reg8_write(client, 0x0d, 0x03); /* unreset gpios */ ++// reg8_write(client, 0x0e, 0xf0); /* unreset gpios */ + } + client->addr = tmp_addr; + ++ mdelay(10); ++ + return 0; +} + -+static int ov495_probe(struct i2c_client *client, ++static int ox03a_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ -+ struct ov495_priv *priv; -+ struct v4l2_ctrl *ctrl; ++ struct ox03a_priv *priv; + int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + -+ v4l2_i2c_subdev_init(&priv->sd, client, &ov495_subdev_ops); ++ v4l2_i2c_subdev_init(&priv->sd, client, &ox03a_subdev_ops); + priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + priv->exposure = 0x100; + priv->gain = 0x100; + priv->autogain = 1; + v4l2_ctrl_handler_init(&priv->hdl, 4); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 16, 1, 7); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_CONTRAST, 0, 16, 1, 7); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_SATURATION, 0, 7, 1, 2); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_HUE, 0, 23, 1, 12); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_GAMMA, -128, 128, 1, 0); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 10, 1, 3); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, priv->autogain); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_GAIN, 0, 0xffff, 1, priv->gain); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xffff, 1, priv->exposure); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, -+ V4L2_CID_HFLIP, 0, 1, 1, 0); -+ v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&priv->hdl, &ox03a_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); -+ ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov495_ctrl_ops, -+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 9); -+ if (ctrl) -+ ctrl->flags &= ~V4L2_CTRL_FLAG_READ_ONLY; + priv->sd.ctrl_handler = &priv->hdl; + + ret = priv->hdl.error; @@ -9363,24 +14347,24 @@ index 0000000..e53c482 + if (ret < 0) + goto cleanup; + -+ ret = ov495_parse_dt(client->dev.of_node, priv); ++ ret = ox03a_parse_dt(client->dev.of_node, priv); + if (ret) + goto cleanup; + -+ ret = ov495_initialize(client); ++ ret = ox03a_initialize(client); + if (ret < 0) + goto cleanup; + + priv->rect.left = 0; + priv->rect.top = 0; -+ priv->rect.width = priv->max_width; -+ priv->rect.height = priv->max_height; ++ priv->rect.width = OX03A_MAX_WIDTH; ++ priv->rect.height = OX03A_MAX_HEIGHT; + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) + goto cleanup; + -+ if (device_create_file(&client->dev, &dev_attr_otp_id_ov495) != 0) { ++ if (device_create_file(&client->dev, &dev_attr_otp_id_ox03a) != 0) { + dev_err(&client->dev, "sysfs otp_id entry creation failed\n"); + goto cleanup; + } @@ -9393,18 +14377,18 @@ index 0000000..e53c482 + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->hdl); + v4l2_device_unregister_subdev(&priv->sd); -+#ifdef CONFIG_SOC_CAMERA_OV495_OV2775 ++#ifdef CONFIG_SOC_CAMERA_OX03A + v4l_err(client, "failed to probe @ 0x%02x (%s)\n", + client->addr, client->adapter->name); +#endif + return ret; +} + -+static int ov495_remove(struct i2c_client *client) ++static int ox03a_remove(struct i2c_client *client) +{ -+ struct ov495_priv *priv = i2c_get_clientdata(client); ++ struct ox03a_priv *priv = i2c_get_clientdata(client); + -+ device_remove_file(&client->dev, &dev_attr_otp_id_ov495); ++ device_remove_file(&client->dev, &dev_attr_otp_id_ox03a); + v4l2_async_unregister_subdev(&priv->sd); + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->hdl); @@ -9413,45 +14397,45 @@ index 0000000..e53c482 + return 0; +} + -+#ifdef CONFIG_SOC_CAMERA_OV495_OV2775 -+static const struct i2c_device_id ov495_id[] = { -+ { "ov495-ov2775", 0 }, ++#ifdef CONFIG_SOC_CAMERA_OX03A ++static const struct i2c_device_id ox03a_id[] = { ++ { "ox03a", 0 }, + { } +}; -+MODULE_DEVICE_TABLE(i2c, ov495_id); ++MODULE_DEVICE_TABLE(i2c, ox03a_id); + -+static const struct of_device_id ov495_of_ids[] = { -+ { .compatible = "ovti,ov495-ov2775", }, ++static const struct of_device_id ox03a_of_ids[] = { ++ { .compatible = "ovti,ox03a", }, + { } +}; -+MODULE_DEVICE_TABLE(of, ov495_of_ids); ++MODULE_DEVICE_TABLE(of, ox03a_of_ids); + -+static struct i2c_driver ov495_i2c_driver = { ++static struct i2c_driver ox03a_i2c_driver = { + .driver = { -+ .name = "ov495-ov2775", -+ .of_match_table = ov495_of_ids, ++ .name = "ox03a", ++ .of_match_table = ox03a_of_ids, + }, -+ .probe = ov495_probe, -+ .remove = ov495_remove, -+ .id_table = ov495_id, ++ .probe = ox03a_probe, ++ .remove = ox03a_remove, ++ .id_table = ox03a_id, +}; + -+module_i2c_driver(ov495_i2c_driver); ++module_i2c_driver(ox03a_i2c_driver); + -+MODULE_DESCRIPTION("SoC Camera driver for OV495-OV2775"); ++MODULE_DESCRIPTION("SoC Camera driver for OX03A"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); +#endif -diff --git a/drivers/media/i2c/soc_camera/ov495_ov2775.h b/drivers/media/i2c/soc_camera/ov495_ov2775.h +diff --git a/drivers/media/i2c/soc_camera/ox03a.h b/drivers/media/i2c/soc_camera/ox03a.h new file mode 100644 -index 0000000..17c94ae +index 0000000..b69273a --- /dev/null -+++ b/drivers/media/i2c/soc_camera/ov495_ov2775.h -@@ -0,0 +1,23 @@ ++++ b/drivers/media/i2c/soc_camera/ox03a.h +@@ -0,0 +1,1724 @@ +/* -+ * OmniVision ov495-ov2775 sensor camera wizard 1920x1080@30/UYVY/MIPI ++ * OmniVision OX03A sensor camera wizard 1920x1080@30/BGGR/MIPI + * -+ * Copyright (C) 2017 Cogent Embedded, Inc. ++ * Copyright (C) 2018 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the @@ -9459,21 +14443,1722 @@ index 0000000..17c94ae + * option) any later version. + */ + -+struct ov495_reg { ++#define OX03A_DISPLAY_PATTERN_COLOR_BAR ++ ++#define OX03A_MAX_WIDTH 1920 ++#define OX03A_MAX_HEIGHT 1080 ++ ++#define OX03A_DELAY 0xffff ++#define OX03A_DT 0x2c /* MIPI Data Type RAW12 */ ++ ++struct ox03a_reg { + u16 reg; + u8 val; +}; + -+static struct ov495_reg ov495_regs_wizard[] = { -+{0x3516, 0x00}, /* unlock write */ -+{0xFFFD, 0x80}, -+{0xFFFE, 0x20}, -+{0x8017, 0x1e | (0 << 6)}, -+{0x7c10, 0x01}, /* UYVY */ ++/* wizard: MIPI 1920x1280 3x12 30fps 750MBPS */ ++static const struct ox03a_reg ox03a_regs_wizard[] = { ++{0x0103, 0x01}, // s/w reset ++{OX03A_DELAY, 10}, // Wait 10ms ++{0x0100, 0x00}, ++{0x0102, 0x00}, ++{0x0103, 0x00}, ++{0x0104, 0x04}, ++{0x0105, 0x00}, ++{0x0106, 0x00}, ++{0x0107, 0x00}, ++{0x0109, 0x00}, ++{0x0300, 0x00}, ++{0x0301, 0x01}, ++{0x0302, 0x00}, ++{0x0303, 0x02}, ++{0x0304, 0x00}, ++{0x0305, 0x3c}, ++{0x0306, 0x00}, ++{0x0307, 0x00}, ++{0x0308, 0x04}, ++{0x0309, 0x02}, ++{0x030a, 0x00}, ++{0x030c, 0x00}, ++{0x030d, 0x00}, ++{0x0310, 0x00}, ++{0x0311, 0x00}, ++{0x0312, 0x00}, ++{0x0313, 0x00}, ++{0x0314, 0x00}, ++{0x0315, 0x00}, ++{0x0316, 0x00}, ++{0x0317, 0x12}, ++{0x0318, 0x01}, ++{0x0320, 0x00}, ++{0x0321, 0x01}, ++{0x0322, 0x00}, ++{0x0323, 0x02}, ++{0x0324, 0x00}, ++{0x0325, 0x6c}, ++{0x0326, 0x00}, ++{0x0327, 0x05}, ++{0x0328, 0x05}, ++{0x0329, 0x01}, ++{0x032a, 0x02}, ++{0x032b, 0x00}, ++{0x032c, 0x00}, ++{0x0400, 0xe7}, ++{0x0401, 0xff}, ++{0x0404, 0x2b}, ++{0x0405, 0x32}, ++{0x0406, 0x33}, ++{0x0407, 0x8f}, ++{0x0408, 0x0c}, ++{0x040a, 0x00}, ++{0x0410, 0xe7}, ++{0x0411, 0xff}, ++{0x0414, 0x2b}, ++{0x0415, 0x32}, ++{0x0416, 0x33}, ++{0x0417, 0x8f}, ++{0x0418, 0x0c}, ++{0x041a, 0x00}, ++{0x2803, 0x00}, ++{0x3000, 0x00}, ++{0x3001, 0x03}, ++{0x3002, 0x03}, ++{0x3003, 0x00}, ++{0x3004, 0x04}, ++{0x3005, 0x00}, ++{0x3006, 0x00}, ++{0x3007, 0x04}, ++{0x3008, 0x00}, ++{0x3009, 0x06}, ++{0x300d, 0x11}, ++{0x300e, 0x11}, ++{0x300f, 0x11}, ++{0x3012, 0x41}, ++{0x3016, 0xf0}, ++{0x3017, 0xf0}, ++{0x3018, 0xf0}, ++{0x3019, 0xf0}, ++{0x301a, 0xf0}, ++{0x301b, 0xb4}, ++{0x301c, 0x01}, ++{0x301d, 0x02}, ++{0x301e, 0xb8}, ++{0x301f, 0xe1}, ++{0x3020, 0x01}, ++{0x3021, 0x00}, ++{0x3022, 0xf8}, ++{0x3023, 0xf0}, ++{0x3024, 0xf0}, ++{0x3025, 0x02}, ++{0x3026, 0x00}, ++{0x3027, 0x00}, ++{0x3028, 0xf0}, ++{0x3029, 0x80}, ++{0x3035, 0x6c}, ++{0x3036, 0x42}, ++{0x3037, 0x20}, ++{0x3038, 0x00}, ++{0x3700, 0x26}, ++{0x3701, 0x1e}, ++{0x3702, 0x25}, ++{0x3703, 0x28}, ++{0x3704, 0x0f}, ++{0x3705, 0x00}, ++{0x3706, 0x39}, ++{0x3707, 0x0a}, ++{0x3708, 0x36}, ++{0x3709, 0x41}, ++{0x370a, 0x00}, ++{0x370b, 0xa3}, ++{0x370c, 0x0f}, ++{0x370d, 0x00}, ++{0x370e, 0xa6}, ++{0x370f, 0x95}, ++{0x3710, 0x15}, ++{0x3711, 0x72}, ++{0x3712, 0x12}, ++{0x3713, 0x00}, ++{0x3714, 0x22}, ++{0x3715, 0x00}, ++{0x3716, 0x04}, ++{0x3717, 0x02}, ++{0x3718, 0x09}, ++{0x3719, 0x1f}, ++{0x371a, 0x0c}, ++{0x371b, 0x16}, ++{0x371c, 0x00}, ++{0x371d, 0x08}, ++{0x371e, 0x00}, ++{0x371f, 0x02}, ++{0x3720, 0x03}, ++{0x3721, 0x1c}, ++{0x3722, 0x87}, ++{0x3723, 0x08}, ++{0x3724, 0x0d}, ++{0x3725, 0x08}, ++{0x3726, 0x0d}, ++{0x3727, 0x08}, ++{0x3728, 0x04}, ++{0x3729, 0x0c}, ++{0x372a, 0x01}, ++{0x372b, 0x01}, ++{0x372c, 0x17}, ++{0x372d, 0x01}, ++{0x372e, 0x35}, ++{0x372f, 0x43}, ++{0x3730, 0x04}, ++{0x3731, 0x06}, ++{0x3732, 0x01}, ++{0x3733, 0x41}, ++{0x3734, 0x0a}, ++{0x3735, 0x11}, ++{0x3736, 0x11}, ++{0x3737, 0x00}, ++{0x3738, 0x54}, ++{0x3739, 0x54}, ++{0x373a, 0x54}, ++{0x373b, 0x54}, ++{0x373c, 0x11}, ++{0x373d, 0x11}, ++{0x373e, 0x00}, ++{0x373f, 0x4c}, ++{0x3740, 0x4c}, ++{0x3741, 0x4c}, ++{0x3742, 0x23}, ++{0x3743, 0x01}, ++{0x3744, 0x16}, ++{0x3745, 0x08}, ++{0x3746, 0x03}, ++{0x3747, 0x01}, ++{0x3748, 0x07}, ++{0x3749, 0x01}, ++{0x374a, 0x07}, ++{0x374b, 0x03}, ++{0x374c, 0xb1}, ++{0x374d, 0x01}, ++{0x374e, 0x01}, ++{0x374f, 0x01}, ++{0x3750, 0x07}, ++{0x3751, 0x02}, ++{0x3752, 0x03}, ++{0x3753, 0xd0}, ++{0x3754, 0x08}, ++{0x3755, 0x00}, ++{0x3758, 0xdd}, ++{0x3759, 0x50}, ++{0x375a, 0x49}, ++{0x375b, 0x02}, ++{0x375c, 0x2f}, ++{0x375d, 0x00}, ++{0x375e, 0x0f}, ++{0x375f, 0x03}, ++{0x3760, 0x13}, ++{0x3761, 0x12}, ++{0x3762, 0x1c}, ++{0x3763, 0x03}, ++{0x3764, 0x0d}, ++{0x3765, 0x25}, ++{0x3766, 0x08}, ++{0x3767, 0x08}, ++{0x3768, 0x21}, ++{0x3769, 0x01}, ++{0x376a, 0x01}, ++{0x376b, 0x00}, ++{0x376c, 0x15}, ++{0x376d, 0x08}, ++{0x376e, 0x08}, ++{0x376f, 0x08}, ++{0x3770, 0x91}, ++{0x3771, 0x00}, ++{0x3772, 0x00}, ++{0x3773, 0x00}, ++{0x3774, 0x02}, ++{0x3775, 0x00}, ++{0x3776, 0x00}, ++{0x3777, 0x00}, ++{0x3778, 0x00}, ++{0x3779, 0x22}, ++{0x377a, 0x00}, ++{0x377b, 0x00}, ++{0x377c, 0x48}, ++{0x377d, 0x00}, ++{0x377e, 0x00}, ++{0x377f, 0x07}, ++{0x3780, 0x00}, ++{0x3781, 0x02}, ++{0x3782, 0x04}, ++{0x3783, 0x02}, ++{0x3784, 0x08}, ++{0x3785, 0x08}, ++{0x3786, 0x00}, ++{0x3787, 0x04}, ++{0x3788, 0x02}, ++{0x3789, 0x02}, ++{0x378a, 0x04}, ++{0x378b, 0x00}, ++{0x378c, 0x00}, ++{0x378d, 0x00}, ++{0x378e, 0x00}, ++{0x378f, 0x00}, ++{0x3790, 0x10}, ++{0x3791, 0x05}, ++{0x3792, 0x31}, ++{0x3793, 0x00}, ++{0x3795, 0x00}, ++{0x3796, 0x00}, ++{0x3797, 0x00}, ++{0x3798, 0x00}, ++{0x3799, 0x00}, ++{0x379a, 0x00}, ++{0x379b, 0x10}, ++{0x379c, 0x01}, ++{0x379d, 0x00}, ++{0x379e, 0x0d}, ++{0x379f, 0x03}, ++{0x37a0, 0x08}, ++{0x37a1, 0x80}, ++{0x37a2, 0x03}, ++{0x37a3, 0x05}, ++{0x37a4, 0x04}, ++{0x37a5, 0x14}, ++{0x37a6, 0x17}, ++{0x37a7, 0x14}, ++{0x37a8, 0x05}, ++{0x37a9, 0x08}, ++{0x37aa, 0x05}, ++{0x37ab, 0x06}, ++{0x37ac, 0x05}, ++{0x37ad, 0x0d}, ++{0x37ae, 0x0d}, ++{0x37af, 0x01}, ++{0x37b0, 0x0c}, ++{0x37b1, 0x05}, ++{0x37b2, 0x08}, ++{0x37b3, 0x0a}, ++{0x37b4, 0x08}, ++{0x37b5, 0x08}, ++{0x37b6, 0x08}, ++{0x37b7, 0x08}, ++{0x37b8, 0xff}, ++{0x37b9, 0x01}, ++{0x37ba, 0x08}, ++{0x37bb, 0x08}, ++{0x37bd, 0x01}, ++{0x37be, 0x01}, ++{0x37bf, 0x01}, ++{0x37c0, 0x01}, ++{0x37c1, 0x11}, ++{0x37c2, 0x11}, ++{0x37c3, 0x00}, ++{0x37c4, 0x63}, ++{0x37c5, 0x63}, ++{0x37c6, 0x63}, ++{0x37c7, 0x34}, ++{0x37c8, 0x21}, ++{0x37c9, 0x00}, ++{0x37ca, 0x08}, ++{0x37cb, 0x00}, ++{0x37cc, 0x40}, ++{0x37cd, 0x00}, ++{0x37ce, 0x01}, ++{0x37cf, 0x08}, ++{0x37d0, 0x00}, ++{0x37d1, 0x39}, ++{0x37d2, 0x00}, ++{0x37d3, 0xa3}, ++{0x37d4, 0x00}, ++{0x37d5, 0x39}, ++{0x37d6, 0x00}, ++{0x37d7, 0xa3}, ++{0x37da, 0x00}, ++{0x37db, 0x00}, ++{0x37dc, 0x00}, ++{0x37dd, 0x00}, ++{0x37de, 0x00}, ++{0x37df, 0x00}, ++{0x37e0, 0x00}, ++{0x37e1, 0x00}, ++{0x37e2, 0x00}, ++{0x37e3, 0x00}, ++{0x37e4, 0x00}, ++{0x37e5, 0x00}, ++{0x37e6, 0x00}, ++{0x37e7, 0x00}, ++{0x37e8, 0x00}, ++{0x37e9, 0x00}, ++{0x37ea, 0x00}, ++{0x37eb, 0x00}, ++{0x37ec, 0x00}, ++{0x37ed, 0x00}, ++{0x37ee, 0x00}, ++{0x37ef, 0x00}, ++{0x37f0, 0x00}, ++{0x37f1, 0x00}, ++{0x37f2, 0x00}, ++{0x37f3, 0x00}, ++{0x37f4, 0x00}, ++{0x37f5, 0x00}, ++{0x37f6, 0x00}, ++{0x37f7, 0x00}, ++{0x37f8, 0x00}, ++{0x37f9, 0x00}, ++{0x37fa, 0x00}, ++{0x37fb, 0x00}, ++{0x37fc, 0x00}, ++{0x37fd, 0x00}, ++{0x37fe, 0x00}, ++{0x37ff, 0x00}, ++{0x3c00, 0x00}, ++{0x3c01, 0x11}, ++{0x3c02, 0x20}, ++{0x3c03, 0x04}, ++{0x3c04, 0x04}, ++{0x3c05, 0x00}, ++{0x3c06, 0x29}, ++{0x3c07, 0x01}, ++{0x3c08, 0x05}, ++{0x3c09, 0x0c}, ++{0x3c0a, 0x04}, ++{0x3c0b, 0xa8}, ++{0x3c0c, 0x11}, ++{0x3c0d, 0x08}, ++{0x3c0e, 0x03}, ++{0x3c0f, 0x02}, ++{0x3c10, 0x01}, ++{0x3c11, 0x08}, ++{0x3c12, 0x89}, ++{0x3c13, 0x21}, ++{0x3c14, 0x81}, ++{0x3c15, 0x21}, ++{0x3c16, 0x11}, ++{0x3c17, 0x01}, ++{0x3c18, 0x0c}, ++{0x3c19, 0x00}, ++{0x3c1a, 0x16}, ++{0x3c1b, 0x81}, ++{0x3c1c, 0x04}, ++{0x3c1d, 0x16}, ++{0x3c1e, 0x11}, ++{0x3c1f, 0x3a}, ++{0x3c20, 0x20}, ++{0x3c21, 0x00}, ++{0x3c22, 0x17}, ++{0x3c23, 0x07}, ++{0x3c24, 0x1a}, ++{0x3c25, 0x1e}, ++{0x3c26, 0x24}, ++{0x3c27, 0x37}, ++{0x3c28, 0x0a}, ++{0x3c29, 0x14}, ++{0x3c2a, 0xd1}, ++{0x3c2b, 0x27}, ++{0x3c2c, 0x33}, ++{0x3c2d, 0x0c}, ++{0x3c2e, 0x12}, ++{0x3c2f, 0x08}, ++{0x3c30, 0x16}, ++{0x3c31, 0x24}, ++{0x3c32, 0x35}, ++{0x3c33, 0x29}, ++{0x3c34, 0x31}, ++{0x3c35, 0x21}, ++{0x3c36, 0x11}, ++{0x3c37, 0x12}, ++{0x3c38, 0x11}, ++{0x3c39, 0x11}, ++{0x3c3a, 0x08}, ++{0x3c3b, 0x38}, ++{0x3c3c, 0x03}, ++{0x3c3d, 0x23}, ++{0x3c3e, 0x05}, ++{0x3c3f, 0x0a}, ++{0x3c40, 0xc1}, ++{0x3c41, 0x04}, ++{0x3c42, 0x01}, ++{0x3c43, 0x18}, ++{0x3c44, 0x21}, ++{0x3c45, 0x20}, ++{0x3c46, 0x0b}, ++{0x3c47, 0x11}, ++{0x3c48, 0x11}, ++{0x3c4a, 0x02}, ++{0x3c4b, 0x63}, ++{0x3c4c, 0x02}, ++{0x3c4d, 0x63}, ++{0x3c4e, 0x00}, ++{0x3c4f, 0x2a}, ++{0x3c50, 0x2a}, ++{0x3c51, 0x2a}, ++{0x3c52, 0x2a}, ++{0x3c53, 0x08}, ++{0x3c54, 0x1d}, ++{0x3c55, 0xeb}, ++{0x3c56, 0x24}, ++{0x3c57, 0x10}, ++{0x3c58, 0x10}, ++{0x3c59, 0x16}, ++{0x3c5a, 0x55}, ++{0x3c5b, 0x25}, ++{0x3c5c, 0x8e}, ++{0x3ce0, 0x00}, ++{0x3ce1, 0x00}, ++{0x3ce2, 0x00}, ++{0x3ce3, 0x00}, ++{0x3ce4, 0x00}, ++{0x3ce5, 0x00}, ++{0x3ce6, 0x00}, ++{0x3ce7, 0x00}, ++{0x3ce8, 0x00}, ++{0x3ce9, 0x00}, ++{0x3cea, 0x00}, ++{0x3ceb, 0x00}, ++{0x3cec, 0x00}, ++{0x3ced, 0x00}, ++{0x3cee, 0x00}, ++{0x3cef, 0x00}, ++{0x3cf0, 0x00}, ++{0x3cf1, 0x00}, ++{0x3cf2, 0x00}, ++{0x3cf3, 0x00}, ++{0x3cf4, 0x00}, ++{0x3cf5, 0x00}, ++{0x3cf6, 0x00}, ++{0x3cf7, 0x00}, ++{0x3cf8, 0x00}, ++{0x3cf9, 0x00}, ++{0x3cfa, 0x00}, ++{0x3cfb, 0x00}, ++{0x3cfc, 0x00}, ++{0x3cfd, 0x00}, ++{0x3cfe, 0x00}, ++{0x3cff, 0x00}, ++{0x3100, 0x00}, ++{0x3101, 0x32}, ++{0x3102, 0x00}, ++{0x3103, 0x25}, ++{0x3104, 0x01}, ++{0x3105, 0x11}, ++{0x3106, 0x10}, ++{0x3107, 0x01}, ++{0x3108, 0x01}, ++{0x3109, 0x00}, ++{0x3182, 0x10}, ++{0x3183, 0xff}, ++{0x3184, 0xff}, ++{0x3187, 0xff}, ++{0x3189, 0x00}, ++{0x318a, 0x00}, ++{0x318b, 0x00}, ++{0x318c, 0x00}, ++{0x318d, 0x00}, ++{0x318e, 0x00}, ++{0x318f, 0x00}, ++{0x3190, 0x00}, ++{0x3191, 0x00}, ++{0x3192, 0x00}, ++{0x3193, 0x00}, ++{0x3194, 0x00}, ++{0x3200, 0x00}, ++{0x3201, 0x08}, ++{0x3202, 0x10}, ++{0x3203, 0x18}, ++{0x3204, 0x20}, ++{0x3205, 0x30}, ++{0x3206, 0x00}, ++{0x3209, 0x00}, ++{0x320a, 0x00}, ++{0x320b, 0x00}, ++{0x320c, 0x00}, ++{0x320d, 0x01}, ++{0x3216, 0x01}, ++{0x3217, 0x00}, ++{0x3218, 0xf7}, ++{0x3219, 0x55}, ++{0x321b, 0x00}, ++{0x3220, 0x1c}, ++{0x3221, 0x00}, ++{0x3304, 0x04}, ++{0x3305, 0x00}, ++{0x3306, 0x03}, ++{0x3307, 0x00}, ++{0x3308, 0x00}, ++{0x3309, 0x00}, ++{0x330a, 0x00}, ++{0x330b, 0x00}, ++{0x330c, 0x00}, ++{0x330d, 0x00}, ++{0x330e, 0x00}, ++{0x330f, 0x00}, ++{0x3310, 0x06}, ++{0x3311, 0x05}, ++{0x3312, 0x55}, ++{0x3313, 0x0a}, ++{0x3314, 0xaa}, ++{0x3315, 0x0f}, ++{0x3316, 0xf0}, ++{0x3317, 0x00}, ++{0x3400, 0x08}, ++{0x3401, 0x00}, ++{0x3402, 0x00}, ++{0x3403, 0xb1}, ++{0x3404, 0x00}, ++{0x3405, 0x0f}, ++{0x3406, 0x08}, ++{0x3407, 0x08}, ++{0x3408, 0x01}, ++{0x3409, 0x02}, ++{0x340a, 0x02}, ++{0x340c, 0x10}, ++{0x340d, 0x00}, ++{0x3410, 0x00}, ++{0x3412, 0x00}, ++{0x3413, 0x00}, ++{0x3414, 0x00}, ++{0x3415, 0x00}, ++{0x3416, 0x00}, ++{0x3417, 0x00}, ++{0x3420, 0x00}, ++{0x3421, 0x00}, ++{0x3422, 0x00}, ++{0x3423, 0x00}, ++{0x3424, 0x00}, ++{0x3425, 0x00}, ++{0x3426, 0x00}, ++{0x3427, 0x00}, ++{0x3428, 0x00}, ++{0x3429, 0x00}, ++{0x342a, 0x00}, ++{0x342b, 0x00}, ++{0x3501, 0x00}, ++{0x3502, 0x24}, ++{0x3503, 0xa8}, ++{0x3504, 0x08}, ++{0x3506, 0x00}, ++{0x3507, 0x00}, ++{0x3508, 0x01}, ++{0x3509, 0x00}, ++{0x350a, 0x01}, ++{0x350b, 0x00}, ++{0x350c, 0x00}, ++{0x350d, 0x00}, ++{0x3541, 0x00}, ++{0x3542, 0x40}, ++{0x3543, 0xa8}, ++{0x3544, 0x08}, ++{0x3546, 0x00}, ++{0x3547, 0x00}, ++{0x3548, 0x01}, ++{0x3549, 0x00}, ++{0x354a, 0x01}, ++{0x354b, 0x00}, ++{0x354c, 0x00}, ++{0x354d, 0x00}, ++{0x3581, 0x00}, ++{0x3582, 0x24}, ++{0x3583, 0xa8}, ++{0x3584, 0x08}, ++{0x3586, 0x00}, ++{0x3587, 0x00}, ++{0x3588, 0x01}, ++{0x3589, 0x00}, ++{0x358a, 0x01}, ++{0x358b, 0x00}, ++{0x358c, 0x00}, ++{0x358d, 0x00}, ++{0x3600, 0x00}, ++{0x3601, 0x70}, ++{0x3602, 0x42}, ++{0x3603, 0xe3}, ++{0x3604, 0x93}, ++{0x3605, 0x40}, ++{0x3606, 0x80}, ++{0x3607, 0x4a}, ++{0x3608, 0x98}, ++{0x3609, 0x70}, ++{0x360a, 0x90}, ++{0x360b, 0x0a}, ++{0x360c, 0x40}, ++{0x360d, 0x88}, ++{0x360e, 0x88}, ++{0x360f, 0x88}, ++{0x3610, 0x89}, ++{0x3611, 0x4f}, ++{0x3612, 0x4f}, ++{0x3613, 0xba}, ++{0x3614, 0x99}, ++{0x3615, 0x99}, ++{0x3616, 0x00}, ++{0x3617, 0x00}, ++{0x3618, 0x18}, ++{0x3619, 0x00}, ++{0x3620, 0x02}, ++{0x3621, 0x80}, ++{0x3622, 0x00}, ++{0x3623, 0x00}, ++{0x3624, 0x00}, ++{0x3625, 0x00}, ++{0x3626, 0x0e}, ++{0x3627, 0x0f}, ++{0x3628, 0x0a}, ++{0x3629, 0x0a}, ++{0x362a, 0x0e}, ++{0x362b, 0x0e}, ++{0x362c, 0x0e}, ++{0x362d, 0x0e}, ++{0x362e, 0x00}, ++{0x362f, 0x00}, ++{0x3630, 0x00}, ++{0x3631, 0x00}, ++{0x3632, 0x99}, ++{0x3633, 0x99}, ++{0x3634, 0x30}, ++{0x3635, 0x30}, ++{0x3636, 0x30}, ++{0x3637, 0x30}, ++{0x3638, 0x00}, ++{0x3639, 0x00}, ++{0x363a, 0x00}, ++{0x363b, 0x0f}, ++{0x363c, 0x0f}, ++{0x363d, 0x0a}, ++{0x363e, 0x0a}, ++{0x363f, 0x0a}, ++{0x3640, 0x0a}, ++{0x3641, 0x0a}, ++{0x3642, 0x0a}, ++{0x3643, 0x10}, ++{0x3644, 0x00}, ++{0x3645, 0x10}, ++{0x3646, 0x16}, ++{0x3647, 0x10}, ++{0x3648, 0x00}, ++{0x3649, 0x13}, ++{0x364a, 0x1d}, ++{0x364b, 0x00}, ++{0x364c, 0x0e}, ++{0x364d, 0x0e}, ++{0x364e, 0x0e}, ++{0x364f, 0x0e}, ++{0x3650, 0x00}, ++{0x3651, 0x00}, ++{0x3652, 0xc5}, ++{0x3653, 0x00}, ++{0x3654, 0x40}, ++{0x3655, 0x00}, ++{0x3656, 0xcf}, ++{0x3657, 0x2b}, ++{0x3658, 0x09}, ++{0x3659, 0x00}, ++{0x365a, 0x00}, ++{0x365b, 0x00}, ++{0x365c, 0x00}, ++{0x365d, 0x00}, ++{0x3660, 0x01}, ++{0x3661, 0x07}, ++{0x3662, 0x00}, ++{0x3663, 0x20}, ++{0x3664, 0x00}, ++{0x3665, 0x12}, ++{0x3666, 0x13}, ++{0x3667, 0x14}, ++{0x3668, 0x95}, ++{0x3669, 0x16}, ++{0x366a, 0x00}, ++{0x366b, 0x00}, ++{0x366c, 0x00}, ++{0x366d, 0x00}, ++{0x366e, 0x00}, ++{0x366f, 0xc4}, ++{0x3670, 0x6f}, ++{0x3671, 0x0b}, ++{0x3672, 0x1d}, ++{0x3673, 0x6a}, ++{0x3674, 0x6f}, ++{0x3675, 0x1d}, ++{0x3676, 0x6f}, ++{0x3677, 0x1d}, ++{0x3678, 0x80}, ++{0x3679, 0x04}, ++{0x367a, 0x00}, ++{0x367b, 0x04}, ++{0x367c, 0x00}, ++{0x367d, 0x00}, ++{0x367e, 0x00}, ++{0x367f, 0x00}, ++{0x3680, 0x00}, ++{0x3800, 0x00}, ++{0x3801, 0x00}, ++{0x3802, 0x00}, ++{0x3803, 0x04}, ++{0x3804, 0x07}, ++{0x3805, 0x8f}, ++{0x3806, 0x05}, ++{0x3807, 0x0b}, ++{0x3808, 0x07}, ++{0x3809, 0x80}, ++{0x380a, 0x05}, ++{0x380b, 0x00}, ++{0x380c, 0x02}, ++{0x380d, 0x14}, ++{0x380e, 0x05}, ++{0x380f, 0x40}, ++{0x3810, 0x00}, ++{0x3811, 0x08}, ++{0x3812, 0x00}, ++{0x3813, 0x04}, ++{0x3814, 0x01}, ++{0x3815, 0x01}, ++{0x3816, 0x01}, ++{0x3817, 0x01}, ++{0x3818, 0x00}, ++{0x3819, 0x00}, ++{0x381a, 0x00}, ++{0x381b, 0x01}, ++{0x381c, 0x08}, ++{0x381d, 0x00}, ++{0x3820, 0x00}, ++{0x3821, 0x20}, ++{0x3822, 0x04}, ++{0x3823, 0x08}, ++{0x3824, 0x00}, ++{0x3825, 0x20}, ++{0x3826, 0x00}, ++{0x3827, 0x08}, ++{0x3828, 0x38}, ++{0x382a, 0x00}, ++{0x382b, 0x00}, ++{0x382c, 0x00}, ++{0x382d, 0x00}, ++{0x3832, 0x00}, ++{0x3833, 0x00}, ++{0x3834, 0x00}, ++{0x3838, 0x00}, ++{0x3839, 0x00}, ++{0x383a, 0x00}, ++{0x383b, 0x00}, ++{0x383c, 0x48}, ++{0x383d, 0x20}, ++{0x383e, 0x00}, ++{0x3842, 0x00}, ++{0x3843, 0x00}, ++{0x3844, 0x00}, ++{0x384c, 0x02}, ++{0x384d, 0x14}, ++{0x384e, 0x00}, ++{0x384f, 0x40}, ++{0x3850, 0x01}, ++{0x3851, 0x02}, ++{0x3852, 0x01}, ++{0x3853, 0x00}, ++{0x3854, 0x00}, ++{0x3855, 0x05}, ++{0x3856, 0x05}, ++{0x3857, 0x33}, ++{0x3858, 0x7c}, ++{0x3859, 0x00}, ++{0x385a, 0x03}, ++{0x385b, 0x05}, ++{0x385c, 0x32}, ++{0x385d, 0x00}, ++{0x385e, 0x12}, ++{0x385f, 0x00}, ++{0x3860, 0x10}, ++{0x3861, 0x00}, ++{0x3862, 0x40}, ++{0x3863, 0x00}, ++{0x3864, 0x40}, ++{0x3865, 0x00}, ++{0x3866, 0x40}, ++{0x3881, 0x02}, ++{0x3882, 0x00}, ++{0x3883, 0x08}, ++{0x3b40, 0x3e}, ++{0x3b41, 0x00}, ++{0x3b42, 0x02}, ++{0x3b43, 0x00}, ++{0x3b44, 0x03}, ++{0x3b45, 0x00}, ++{0x3b46, 0x03}, ++{0x3b47, 0x00}, ++{0x3b80, 0x00}, ++{0x3b81, 0x00}, ++{0x3b82, 0x07}, ++{0x3b83, 0x87}, ++{0x3b84, 0x36}, ++{0x3b85, 0x00}, ++{0x3b86, 0x00}, ++{0x3b87, 0x04}, ++{0x3b88, 0x00}, ++{0x3b89, 0x04}, ++{0x3b8a, 0x00}, ++{0x3b8b, 0x0a}, ++{0x3b8c, 0x00}, ++{0x3b8d, 0x01}, ++{0x3b8e, 0x03}, ++{0x3b8f, 0xe8}, ++{0x3d82, 0xbc}, ++{0x3d83, 0x08}, ++{0x3d84, 0x00}, ++{0x3d85, 0x1b}, ++{0x3d86, 0x02}, ++{0x3d87, 0x0a}, ++{0x3d88, 0x00}, ++{0x3d89, 0x00}, ++{0x3d8a, 0x03}, ++{0x3d8b, 0xff}, ++{0x3d8c, 0x00}, ++{0x3d8d, 0x00}, ++{0x3d90, 0x00}, ++{0x3d91, 0x00}, ++{0x3d92, 0xe2}, ++{0x3d93, 0x46}, ++{0x3d94, 0x14}, ++{0x3d95, 0x06}, ++{0x3d96, 0x01}, ++{0x3d97, 0x00}, ++{0x3d98, 0x00}, ++{0x3d99, 0x00}, ++{0x3d9a, 0x00}, ++{0x3d9b, 0x00}, ++{0x3d9c, 0x00}, ++{0x3d9d, 0x00}, ++{0x3d9e, 0x00}, ++{0x3d9f, 0x00}, ++{0x3da0, 0x00}, ++{0x3da1, 0x00}, ++{0x3da2, 0x00}, ++{0x3da4, 0x00}, ++{0x3e00, 0x00}, ++{0x3e01, 0x00}, ++{0x3e02, 0x0f}, ++{0x3e03, 0xdb}, ++{0x3e04, 0x14}, ++{0x3e05, 0x00}, ++{0x3e06, 0x03}, ++{0x3e07, 0x40}, ++{0x3e08, 0x00}, ++{0x3e09, 0x00}, ++{0x3e0a, 0x00}, ++{0x3e0b, 0x00}, ++{0x3e0c, 0x00}, ++{0x3e0d, 0x00}, ++{0x3e0e, 0x00}, ++{0x3f00, 0x04}, ++{0x3f01, 0x00}, ++{0x3f02, 0x00}, ++{0x3f03, 0x01}, ++{0x4000, 0xf8}, ++{0x4001, 0xeb}, ++{0x4004, 0x00}, ++{0x4005, 0x40}, ++{0x4006, 0x00}, ++{0x4007, 0x10}, ++{0x4008, 0x00}, ++{0x4009, 0x05}, ++{0x400a, 0x02}, ++{0x400b, 0x00}, ++{0x400c, 0x00}, ++{0x400d, 0x10}, ++{0x400e, 0x00}, ++{0x400f, 0xa0}, ++{0x4010, 0x10}, ++{0x4011, 0xff}, ++{0x4012, 0x08}, ++{0x4013, 0x02}, ++{0x4014, 0x02}, ++{0x4015, 0x02}, ++{0x4016, 0x00}, ++{0x4017, 0x04}, ++{0x4018, 0x18}, ++{0x4019, 0x04}, ++{0x401a, 0x58}, ++{0x4020, 0x00}, ++{0x4021, 0x00}, ++{0x4022, 0x00}, ++{0x4023, 0x00}, ++{0x4024, 0x00}, ++{0x4025, 0x00}, ++{0x4026, 0x00}, ++{0x4027, 0x00}, ++{0x4028, 0x4f}, ++{0x4029, 0x01}, ++{0x402a, 0x00}, ++{0x402b, 0x00}, ++{0x402c, 0x00}, ++{0x402d, 0x00}, ++{0x402e, 0x00}, ++{0x402f, 0x40}, ++{0x4030, 0x00}, ++{0x4031, 0x40}, ++{0x4032, 0x9e}, ++{0x4033, 0x80}, ++{0x4034, 0x00}, ++{0x4035, 0x80}, ++{0x4036, 0x00}, ++{0x4037, 0x80}, ++{0x4038, 0x00}, ++{0x4039, 0x80}, ++{0x403a, 0x00}, ++{0x403b, 0x80}, ++{0x403c, 0x00}, ++{0x403d, 0x00}, ++{0x4040, 0x00}, ++{0x4041, 0x00}, ++{0x4042, 0x00}, ++{0x4043, 0x00}, ++{0x4044, 0x00}, ++{0x4045, 0x00}, ++{0x4046, 0x00}, ++{0x4047, 0x00}, ++{0x4048, 0x00}, ++{0x4049, 0x00}, ++{0x404a, 0x00}, ++{0x404b, 0x00}, ++{0x404c, 0x00}, ++{0x404d, 0x00}, ++{0x404e, 0x00}, ++{0x404f, 0x00}, ++{0x4050, 0x00}, ++{0x4051, 0x05}, ++{0x4052, 0x00}, ++{0x4053, 0x80}, ++{0x4054, 0x00}, ++{0x4055, 0x80}, ++{0x4056, 0x00}, ++{0x4057, 0x80}, ++{0x4058, 0x00}, ++{0x4059, 0x80}, ++{0x405a, 0x30}, ++{0x405b, 0x18}, ++{0x405c, 0x00}, ++{0x405d, 0x00}, ++{0x405e, 0x00}, ++{0x405f, 0x00}, ++{0x4060, 0x00}, ++{0x4065, 0x00}, ++{0x4066, 0x02}, ++{0x406d, 0x00}, ++{0x406e, 0x00}, ++{0x406f, 0x00}, ++{0x40a0, 0x00}, ++{0x40a1, 0x00}, ++{0x40a2, 0x00}, ++{0x40a3, 0x00}, ++{0x40a4, 0x00}, ++{0x40a5, 0x00}, ++{0x40a6, 0x00}, ++{0x40a7, 0x00}, ++{0x40c0, 0x00}, ++{0x40c1, 0x00}, ++{0x40c2, 0x00}, ++{0x40c3, 0x00}, ++{0x40c4, 0x00}, ++{0x40c5, 0x00}, ++{0x40c6, 0x00}, ++{0x40c7, 0x00}, ++{0x40c8, 0x00}, ++{0x40c9, 0x00}, ++{0x40ca, 0x00}, ++{0x40cb, 0x00}, ++{0x40cc, 0x00}, ++{0x40cd, 0x00}, ++{0x40ce, 0x00}, ++{0x40cf, 0x00}, ++{0x4200, 0x00}, ++{0x4201, 0x00}, ++{0x4202, 0x00}, ++{0x4203, 0x00}, ++{0x4204, 0x00}, ++{0x4205, 0x00}, ++{0x4206, 0x00}, ++{0x4207, 0x00}, ++{0x4208, 0x00}, ++{0x4300, 0x00}, ++{0x4301, 0x00}, ++{0x4302, 0x00}, ++{0x4303, 0x00}, ++{0x4304, 0x00}, ++{0x4305, 0x00}, ++{0x4306, 0x00}, ++{0x4307, 0x00}, ++{0x4308, 0x00}, ++{0x4309, 0x00}, ++{0x430a, 0x00}, ++{0x430b, 0xff}, ++{0x430c, 0xff}, ++{0x430d, 0x00}, ++{0x430e, 0x00}, ++{0x430f, 0x02}, ++{0x4500, 0x16}, ++{0x4501, 0x18}, ++{0x4502, 0x00}, ++{0x4503, 0x00}, ++{0x4504, 0x01}, ++{0x4505, 0x00}, ++{0x4506, 0x32}, ++{0x4507, 0x07}, ++{0x4508, 0x1a}, ++{0x4580, 0x68}, ++{0x4581, 0xc7}, ++{0x4582, 0x07}, ++{0x4583, 0x07}, ++{0x4584, 0xec}, ++{0x4585, 0x09}, ++{0x4586, 0xae}, ++{0x4587, 0x04}, ++{0x4588, 0x52}, ++{0x4589, 0x05}, ++{0x458a, 0x47}, ++{0x458b, 0x02}, ++{0x458c, 0xe2}, ++{0x458d, 0x03}, ++{0x458e, 0x85}, ++{0x458f, 0x00}, ++{0x4590, 0x20}, ++{0x4591, 0x09}, ++{0x4592, 0x60}, ++{0x45a6, 0x18}, ++{0x4600, 0x00}, ++{0x4601, 0x30}, ++{0x4602, 0x00}, ++{0x4603, 0x01}, ++{0x4604, 0x00}, ++{0x4605, 0x03}, ++{0x4609, 0x00}, ++{0x460a, 0x30}, ++{0x460b, 0x00}, ++{0x460c, 0x30}, ++{0x460d, 0x01}, ++{0x460e, 0x00}, ++{0x4700, 0x2a}, ++{0x4702, 0x00}, ++{0x4703, 0x80}, ++{0x4704, 0x00}, ++{0x4705, 0x10}, ++{0x4706, 0xaa}, ++{0x4707, 0x55}, ++{0x4708, 0x99}, ++{0x4709, 0x66}, ++{0x470a, 0x08}, ++{0x470b, 0x88}, ++{0x470c, 0x00}, ++{0x470d, 0x02}, ++{0x470e, 0x00}, ++{0x470f, 0x00}, ++{0x4710, 0x00}, ++{0x4711, 0x00}, ++{0x4712, 0x00}, ++{0x4713, 0x00}, ++{0x4800, 0x04}, ++{0x4802, 0x00}, ++{0x4803, 0x00}, ++{0x4804, 0x08}, ++{0x4805, 0x00}, ++{0x4806, 0x00}, ++{0x4807, 0x03}, ++{0x4808, 0x18}, ++{0x480e, 0x04}, ++{0x4810, 0xff}, ++{0x4811, 0xff}, ++{0x4813, 0xe4}, // VC ++{0x4814, 0x2a}, ++{0x4815, 0x2b}, ++{0x4816, 0x2b}, ++{0x4818, 0x00}, ++{0x4819, 0x70}, ++{0x481a, 0x00}, ++{0x481b, 0x3c}, ++{0x481c, 0x01}, ++{0x481d, 0x2c}, ++{0x481e, 0x5f}, ++{0x481f, 0x26}, ++{0x4820, 0x00}, ++{0x4821, 0x3c}, ++{0x4822, 0x00}, ++{0x4823, 0x3c}, ++{0x4824, 0x00}, ++{0x4825, 0x32}, ++{0x4826, 0x32}, ++{0x4827, 0x55}, ++{0x4828, 0x00}, ++{0x4829, 0x64}, ++{0x482a, 0x06}, ++{0x482b, 0x04}, ++{0x482c, 0x00}, ++{0x482d, 0x00}, ++{0x482e, 0x34}, ++{0x482f, 0x00}, ++{0x4830, 0x00}, ++{0x4831, 0x64}, ++{0x4832, 0x00}, ++{0x4833, 0x10}, ++{0x4837, 0x15}, ++{0x4838, 0x00}, ++{0x4839, 0x00}, ++{0x483c, 0x10}, ++{0x483d, 0x00}, ++{0x484a, 0x3f}, ++{0x484b, 0x67}, ++{0x484c, 0x00}, ++{0x484e, 0x10}, ++{0x4850, 0x40}, ++{0x4851, 0xaa}, ++{0x4852, 0xff}, ++{0x4853, 0x8a}, ++{0x4854, 0x08}, ++{0x4855, 0x30}, ++{0x4856, 0x01}, ++{0x4860, 0x00}, ++{0x4861, 0xa0}, ++{0x4862, 0x01}, ++{0x4863, 0x01}, ++{0x4864, 0x02}, ++{0x4865, 0x66}, ++{0x4866, 0x99}, ++{0x4867, 0x88}, ++{0x4868, 0xaa}, ++{0x4869, 0xff}, ++{0x486a, 0x3f}, ++{0x486b, 0x84}, ++{0x486c, 0x36}, ++{0x486d, 0x00}, ++{0x486e, 0x84}, ++{0x486f, 0x36}, ++{0x4870, 0x00}, ++{0x4880, 0x00}, ++{0x4881, 0x00}, ++{0x4882, 0x00}, ++{0x4883, 0x00}, ++{0x4884, 0x08}, ++{0x4885, 0x00}, ++{0x4886, 0x00}, ++{0x4900, 0x08}, ++{0x4901, 0x00}, ++{0x4902, 0x00}, ++{0x4903, 0x80}, ++{0x4f00, 0xff}, ++{0x4f01, 0xff}, ++{0x4f04, 0x00}, ++{0x4f05, 0x01}, ++{0x5180, 0x04}, ++{0x5181, 0x00}, ++{0x5182, 0x04}, ++{0x5183, 0x00}, ++{0x5184, 0x04}, ++{0x5185, 0x00}, ++{0x5186, 0x04}, ++{0x5187, 0x00}, ++{0x5188, 0x00}, ++{0x5189, 0x00}, ++{0x518a, 0x00}, ++{0x518b, 0x10}, ++{0x51a0, 0x04}, ++{0x51a1, 0x00}, ++{0x51a2, 0x04}, ++{0x51a3, 0x00}, ++{0x51a4, 0x04}, ++{0x51a5, 0x00}, ++{0x51a6, 0x04}, ++{0x51a7, 0x00}, ++{0x51a8, 0x00}, ++{0x51a9, 0x00}, ++{0x51aa, 0x00}, ++{0x51ab, 0x10}, ++{0x51c0, 0x04}, ++{0x51c1, 0x00}, ++{0x51c2, 0x04}, ++{0x51c3, 0x00}, ++{0x51c4, 0x04}, ++{0x51c5, 0x00}, ++{0x51c6, 0x04}, ++{0x51c7, 0x00}, ++{0x51c8, 0x00}, ++{0x51c9, 0x00}, ++{0x51ca, 0x00}, ++{0x51cb, 0x10}, ++{0x5380, 0x19}, ++{0x5381, 0x94}, ++{0x5382, 0x2e}, ++{0x5383, 0x24}, ++{0x5384, 0x12}, ++{0x5385, 0x41}, ++{0x5386, 0x48}, ++{0x5387, 0x84}, ++{0x5388, 0x40}, ++{0x5389, 0x00}, ++{0x538a, 0x00}, ++{0x538b, 0x03}, ++{0x538c, 0x00}, ++{0x538d, 0x0f}, ++{0x538e, 0x00}, ++{0x538f, 0x3f}, ++{0x5390, 0x0f}, ++{0x5391, 0xfd}, ++{0x5392, 0xf5}, ++{0x5393, 0xf5}, ++{0x5394, 0x02}, ++{0x5395, 0xff}, ++{0x5396, 0x00}, ++{0x5397, 0x00}, ++{0x53a0, 0x41}, ++{0x53a2, 0x04}, ++{0x53a3, 0x00}, ++{0x53a4, 0x04}, ++{0x53a5, 0x00}, ++{0x53a6, 0x04}, ++{0x53a7, 0x00}, ++{0x53ac, 0x04}, ++{0x53ad, 0x00}, ++{0x53ae, 0x04}, ++{0x53af, 0x00}, ++{0x53b0, 0x04}, ++{0x53b1, 0x00}, ++{0x5400, 0x19}, ++{0x5401, 0x94}, ++{0x5402, 0x2e}, ++{0x5403, 0x24}, ++{0x5404, 0x12}, ++{0x5405, 0x41}, ++{0x5406, 0x48}, ++{0x5407, 0x84}, ++{0x5408, 0x40}, ++{0x5409, 0x00}, ++{0x540a, 0x00}, ++{0x540b, 0x03}, ++{0x540c, 0x00}, ++{0x540d, 0x0f}, ++{0x540e, 0x00}, ++{0x540f, 0x3f}, ++{0x5410, 0x0f}, ++{0x5411, 0xfd}, ++{0x5412, 0xf5}, ++{0x5413, 0xf5}, ++{0x5414, 0x02}, ++{0x5415, 0xff}, ++{0x5416, 0x00}, ++{0x5417, 0x00}, ++{0x5420, 0x41}, ++{0x5422, 0x04}, ++{0x5423, 0x00}, ++{0x5424, 0x04}, ++{0x5425, 0x00}, ++{0x5426, 0x04}, ++{0x5427, 0x00}, ++{0x542c, 0x04}, ++{0x542d, 0x00}, ++{0x542e, 0x04}, ++{0x542f, 0x00}, ++{0x5430, 0x04}, ++{0x5431, 0x00}, ++{0x5480, 0x19}, ++{0x5481, 0x94}, ++{0x5482, 0x2e}, ++{0x5483, 0x24}, ++{0x5484, 0x12}, ++{0x5485, 0x41}, ++{0x5486, 0x48}, ++{0x5487, 0x84}, ++{0x5488, 0x40}, ++{0x5489, 0x00}, ++{0x548a, 0x00}, ++{0x548b, 0x03}, ++{0x548c, 0x00}, ++{0x548d, 0x0f}, ++{0x548e, 0x00}, ++{0x548f, 0x3f}, ++{0x5490, 0x0f}, ++{0x5491, 0xfd}, ++{0x5492, 0xf5}, ++{0x5493, 0xf5}, ++{0x5494, 0x02}, ++{0x5495, 0xff}, ++{0x5496, 0x00}, ++{0x5497, 0x00}, ++{0x54a0, 0x41}, ++{0x54a2, 0x04}, ++{0x54a3, 0x00}, ++{0x54a4, 0x04}, ++{0x54a5, 0x00}, ++{0x54a6, 0x04}, ++{0x54a7, 0x00}, ++{0x54ac, 0x04}, ++{0x54ad, 0x00}, ++{0x54ae, 0x04}, ++{0x54af, 0x00}, ++{0x54b0, 0x04}, ++{0x54b1, 0x00}, ++{0x5800, 0x19}, ++{0x5801, 0x03}, ++{0x5802, 0x60}, ++{0x5803, 0xf0}, ++{0x5804, 0x00}, ++{0x5805, 0x40}, ++{0x5806, 0x01}, ++{0x5807, 0x00}, ++{0x5808, 0x60}, ++{0x5809, 0xf0}, ++{0x580a, 0x33}, ++{0x580b, 0x10}, ++{0x580c, 0x04}, ++{0x580d, 0x00}, ++{0x580e, 0x10}, ++{0x580f, 0x10}, ++{0x5810, 0x02}, ++{0x5811, 0x08}, ++{0x5812, 0x38}, ++{0x5813, 0x00}, ++{0x5814, 0x00}, ++{0x5815, 0x00}, ++{0x5816, 0x00}, ++{0x5000, 0x81}, ++{0x5001, 0x42}, ++{0x5002, 0x19}, ++{0x5003, 0x16}, ++{0x5004, 0x02}, ++{0x5005, 0x00}, ++{0x5006, 0x01}, ++{0x5007, 0x00}, ++{0x5008, 0x00}, ++{0x5009, 0x40}, ++{0x500a, 0x00}, ++{0x500b, 0x00}, ++{0x500c, 0x00}, ++{0x500d, 0x00}, ++{0x500e, 0x00}, ++{0x500f, 0x00}, ++{0x5010, 0x07}, ++{0x5011, 0x8f}, ++{0x5012, 0x05}, ++{0x5013, 0x0f}, ++{0x5014, 0x01}, ++{0x5015, 0x01}, ++{0x5016, 0x01}, ++{0x5017, 0x01}, ++{0x5018, 0x00}, ++{0x5019, 0x00}, ++{0x501a, 0x00}, ++{0x501b, 0x10}, ++{0x501c, 0x00}, ++{0x501d, 0x10}, ++{0x501e, 0x00}, ++{0x501f, 0x10}, ++{0x5020, 0x04}, ++{0x5021, 0x00}, ++{0x5022, 0x04}, ++{0x5023, 0x00}, ++{0x5024, 0x04}, ++{0x5025, 0x00}, ++{0x5026, 0x00}, ++{0x5027, 0x10}, ++{0x5028, 0x00}, ++{0x5029, 0x10}, ++{0x502a, 0x00}, ++{0x502b, 0x10}, ++{0x502c, 0x00}, ++{0x502d, 0x10}, ++{0x502e, 0x00}, ++{0x502f, 0x10}, ++{0x5030, 0x00}, ++{0x5031, 0x10}, ++{0x5032, 0x04}, ++{0x5033, 0x00}, ++{0x5034, 0x04}, ++{0x5035, 0x00}, ++{0x5036, 0x04}, ++{0x5037, 0x00}, ++{0x5038, 0x00}, ++{0x5039, 0x10}, ++{0x503a, 0x00}, ++{0x503b, 0x10}, ++{0x503c, 0x00}, ++{0x503d, 0x10}, ++{0x503e, 0x00}, ++{0x503f, 0x00}, ++{0x5040, 0x00}, ++{0x5041, 0x01}, ++{0x5042, 0x00}, ++{0x5043, 0x00}, ++{0x5600, 0x0f}, ++{0x5601, 0xab}, ++{0x5602, 0x02}, ++{0x5603, 0x58}, ++{0x5604, 0x03}, ++{0x5605, 0x20}, ++{0x5606, 0x02}, ++{0x5607, 0x58}, ++{0x5608, 0x03}, ++{0x5609, 0x20}, ++{0x560a, 0x02}, ++{0x560b, 0x58}, ++{0x560c, 0x03}, ++{0x560d, 0x20}, ++{0x560e, 0x02}, ++{0x560f, 0x58}, ++{0x5610, 0x03}, ++{0x5611, 0x20}, ++{0x5612, 0x02}, ++{0x5613, 0x58}, ++{0x5614, 0x03}, ++{0x5615, 0x20}, ++{0x5616, 0x02}, ++{0x5617, 0x58}, ++{0x5618, 0x03}, ++{0x5619, 0x20}, ++{0x5640, 0x0f}, ++{0x5641, 0xab}, ++{0x5642, 0x02}, ++{0x5643, 0x58}, ++{0x5644, 0x03}, ++{0x5645, 0x20}, ++{0x5646, 0x02}, ++{0x5647, 0x58}, ++{0x5648, 0x03}, ++{0x5649, 0x20}, ++{0x564a, 0x02}, ++{0x564b, 0x58}, ++{0x564c, 0x03}, ++{0x564d, 0x20}, ++{0x564e, 0x02}, ++{0x564f, 0x58}, ++{0x5650, 0x03}, ++{0x5651, 0x20}, ++{0x5652, 0x02}, ++{0x5653, 0x58}, ++{0x5654, 0x03}, ++{0x5655, 0x20}, ++{0x5656, 0x02}, ++{0x5657, 0x58}, ++{0x5658, 0x03}, ++{0x5659, 0x20}, ++{0x5680, 0x0f}, ++{0x5681, 0xab}, ++{0x5682, 0x02}, ++{0x5683, 0x58}, ++{0x5684, 0x03}, ++{0x5685, 0x20}, ++{0x5686, 0x02}, ++{0x5687, 0x58}, ++{0x5688, 0x03}, ++{0x5689, 0x20}, ++{0x568a, 0x02}, ++{0x568b, 0x58}, ++{0x568c, 0x03}, ++{0x568d, 0x20}, ++{0x568e, 0x02}, ++{0x568f, 0x58}, ++{0x5690, 0x03}, ++{0x5691, 0x20}, ++{0x5692, 0x02}, ++{0x5693, 0x58}, ++{0x5694, 0x03}, ++{0x5695, 0x20}, ++{0x5696, 0x02}, ++{0x5697, 0x58}, ++{0x5698, 0x03}, ++{0x5699, 0x20}, ++{0x5700, 0x00}, ++{0x5701, 0x00}, ++{0x5702, 0x00}, ++{0x5703, 0x00}, ++{0x5704, 0x02}, ++{0x5705, 0x80}, ++{0x5706, 0x01}, ++{0x5707, 0xe0}, ++{0x5708, 0x00}, ++{0x5709, 0x0e}, ++{0x5740, 0x00}, ++{0x5741, 0x00}, ++{0x5742, 0x00}, ++{0x5743, 0x00}, ++{0x5744, 0x02}, ++{0x5745, 0x80}, ++{0x5746, 0x01}, ++{0x5747, 0xe0}, ++{0x5748, 0x00}, ++{0x5749, 0x0e}, ++{0x5780, 0x00}, ++{0x5781, 0x00}, ++{0x5782, 0x00}, ++{0x5783, 0x00}, ++{0x5784, 0x02}, ++{0x5785, 0x80}, ++{0x5786, 0x01}, ++{0x5787, 0xe0}, ++{0x5788, 0x00}, ++{0x5789, 0x0e}, ++{0x5200, 0x70}, ++{0x5201, 0x80}, ++{0x5202, 0x73}, ++{0x5203, 0xff}, ++{0x5204, 0x02}, ++{0x5205, 0x6c}, ++{0x5206, 0x00}, ++{0x5207, 0x00}, ++{0x5209, 0x08}, ++{0x520a, 0x00}, ++{0x520b, 0x07}, ++{0x520c, 0x01}, ++{0x520d, 0x01}, ++{0x520e, 0x01}, ++{0x520f, 0x01}, ++{0x5210, 0x00}, ++{0x5211, 0x00}, ++{0x5212, 0x00}, ++{0x5213, 0x00}, ++{0x5214, 0x00}, ++{0x5215, 0x00}, ++{0x5216, 0x07}, ++{0x5217, 0x8b}, ++{0x5218, 0x00}, ++{0x5219, 0x00}, ++{0x5280, 0x00}, ++{0x5281, 0x00}, ++{0x5282, 0xff}, ++{0x5283, 0xff}, ++{0x5284, 0x02}, ++{0x5285, 0x6c}, ++{0x5286, 0x00}, ++{0x5287, 0x00}, ++{0x5289, 0x08}, ++{0x528a, 0x00}, ++{0x528b, 0x07}, ++{0x528c, 0x01}, ++{0x528d, 0x01}, ++{0x528e, 0x01}, ++{0x528f, 0x01}, ++{0x5290, 0x00}, ++{0x5291, 0x00}, ++{0x5292, 0x00}, ++{0x5293, 0x00}, ++{0x5294, 0x00}, ++{0x5295, 0x00}, ++{0x5296, 0x07}, ++{0x5297, 0x8b}, ++{0x5298, 0x00}, ++{0x5299, 0x00}, ++{0x5300, 0x00}, ++{0x5301, 0x00}, ++{0x5302, 0xff}, ++{0x5303, 0xff}, ++{0x5304, 0x02}, ++{0x5305, 0x6c}, ++{0x5306, 0x00}, ++{0x5307, 0x00}, ++{0x5309, 0x08}, ++{0x530a, 0x00}, ++{0x530b, 0x07}, ++{0x530c, 0x01}, ++{0x530d, 0x01}, ++{0x530e, 0x01}, ++{0x530f, 0x01}, ++{0x5310, 0x00}, ++{0x5311, 0x00}, ++{0x5312, 0x00}, ++{0x5313, 0x00}, ++{0x5314, 0x00}, ++{0x5315, 0x00}, ++{0x5316, 0x07}, ++{0x5317, 0x8b}, ++{0x5318, 0x00}, ++{0x5319, 0x00}, ++#ifdef OX03A_DISPLAY_PATTERN_COLOR_BAR ++{0x5080, 0x80}, /* Rolling test pattern for HCG */ ++#else ++{0x5080, 0x00}, ++#endif ++{0x5081, 0x01}, ++{0x5082, 0xb0}, ++{0x5083, 0x0f}, ++{0x5084, 0x00}, ++{0x5085, 0x00}, ++{0x5086, 0x00}, ++{0x5087, 0x01}, ++{0x5088, 0x00}, ++{0x5089, 0x00}, ++{0x508a, 0x00}, ++{0x508b, 0x00}, ++{0x508c, 0x00}, ++{0x508d, 0x00}, ++{0x508e, 0x00}, ++{0x508f, 0x00}, ++{0x5090, 0x00}, ++{0x5091, 0x00}, ++{0x5092, 0x00}, ++{0x5093, 0x00}, ++{0x5094, 0x00}, ++{0x5095, 0x00}, ++{0x5096, 0x00}, ++{0x5097, 0x00}, ++#ifdef OX03A_DISPLAY_PATTERN_COLOR_BAR ++{0x50c0, 0x80}, /* Rolling test pattern for LCG */ ++#else ++{0x50c0, 0x00}, ++#endif ++{0x50c1, 0x01}, ++{0x50c2, 0xb0}, ++{0x50c3, 0x0f}, ++{0x50c4, 0x00}, ++{0x50c5, 0x00}, ++{0x50c6, 0x00}, ++{0x50c7, 0x01}, ++{0x50c8, 0x00}, ++{0x50c9, 0x00}, ++{0x50ca, 0x00}, ++{0x50cb, 0x00}, ++{0x50cc, 0x00}, ++{0x50cd, 0x00}, ++{0x50ce, 0x00}, ++{0x50cf, 0x00}, ++{0x50d0, 0x00}, ++{0x50d1, 0x00}, ++{0x50d2, 0x00}, ++{0x50d3, 0x00}, ++{0x50d4, 0x00}, ++{0x50d5, 0x00}, ++{0x50d6, 0x00}, ++{0x50d7, 0x00}, ++#ifdef OX03A_DISPLAY_PATTERN_COLOR_BAR ++{0x5100, 0x80}, /* Rolling test pattern for VS */ ++#else ++{0x5100, 0x00}, ++#endif ++{0x5101, 0x01}, ++{0x5102, 0xb0}, ++{0x5103, 0x0f}, ++{0x5104, 0x00}, ++{0x5105, 0x00}, ++{0x5106, 0x00}, ++{0x5107, 0x01}, ++{0x5108, 0x00}, ++{0x5109, 0x00}, ++{0x510a, 0x00}, ++{0x510b, 0x00}, ++{0x510c, 0x00}, ++{0x510d, 0x00}, ++{0x510e, 0x00}, ++{0x510f, 0x00}, ++{0x5110, 0x00}, ++{0x5111, 0x00}, ++{0x5112, 0x00}, ++{0x5113, 0x00}, ++{0x5114, 0x00}, ++{0x5115, 0x00}, ++{0x5116, 0x00}, ++{0x5117, 0x00}, ++{0x380e, 0x05}, ++{0x380f, 0x34}, ++{0x380c, 0x06}, ++{0x380d, 0xcc}, ++{0x384c, 0x03}, ++{0x384d, 0xc0}, ++{0x4603, 0x01}, ++{0x4601, 0x00}, ++{0x3501, 0x01}, ++{0x0100, 0x01}, +}; diff --git a/drivers/media/i2c/soc_camera/ti9x4.c b/drivers/media/i2c/soc_camera/ti9x4.c new file mode 100644 -index 0000000..a4c7b3f +index 0000000..222db9b --- /dev/null +++ b/drivers/media/i2c/soc_camera/ti9x4.c @@ -0,0 +1,518 @@ @@ -9698,7 +16383,7 @@ index 0000000..a4c7b3f + reg8_write(client, 0x6d, port_config); + reg8_write(client, 0x7c, port_config2); + reg8_write(client, 0x70, (idx << 6) | 0x1e); /* CSI data type: yuv422 8-bit, assign VC */ -+ reg8_write(client, 0x71, (idx << 6) | 0x2a); /* CSI data type: RAW8, assign VC */ ++ reg8_write(client, 0x71, (idx << 6) | 0x2c); /* CSI data type: RAW12, assign VC */ + reg8_write(client, 0xbc, 0x00); /* Setup minimal time between FV and LV to 3 PCLKs */ + reg8_write(client, 0x6e, 0x88); /* Sensor reset: backchannel GPIO0/GPIO1 set low */ +} @@ -10158,7 +16843,7 @@ index 0000000..b53b4c6 +} +#endif /* _TI9X4_H */ diff --git a/drivers/media/platform/soc_camera/rcar_csi2.c b/drivers/media/platform/soc_camera/rcar_csi2.c -index 4d95da6..2ef27e8 100644 +index 4d95da6..53fc644 100644 --- a/drivers/media/platform/soc_camera/rcar_csi2.c +++ b/drivers/media/platform/soc_camera/rcar_csi2.c @@ -37,8 +37,9 @@ @@ -10191,6 +16876,15 @@ index 4d95da6..2ef27e8 100644 #define RCAR_CSI2_PHYCNT_SHUTDOWNZ (1 << 17) #define RCAR_CSI2_PHYCNT_RSTZ (1 << 16) #define RCAR_CSI2_PHYCNT_ENABLECLK (1 << 4) +@@ -80,7 +86,7 @@ + + #define RCAR_CSI2_VCDT_VCDTN_EN (1 << 15) + #define RCAR_CSI2_VCDT_SEL_VCN (1 << 8) +-#define RCAR_CSI2_VCDT_SEL_DTN_ON (1 << 6) ++#define RCAR_CSI2_VCDT_SEL_DTN_ON (0 << 6) + #define RCAR_CSI2_VCDT_SEL_DTN (1 << 0) + + #define RCAR_CSI2_LINKCNT_MONITOR_EN (1 << 31) @@ -106,6 +112,9 @@ #define RCAR_CSI2_LSWAP_L0SEL_PLANE2 (2 << 0) #define RCAR_CSI2_LSWAP_L0SEL_PLANE3 (3 << 0) @@ -10524,7 +17218,7 @@ index 4d95da6..2ef27e8 100644 } return ret; -@@ -543,18 +642,19 @@ static int rcar_csi2_parse_dt(struct device_node *np, +@@ -543,39 +642,30 @@ static int rcar_csi2_parse_dt(struct device_node *np, return -EINVAL; v4l2_of_parse_endpoint(endpoint, &bus_cfg); @@ -10550,25 +17244,42 @@ index 4d95da6..2ef27e8 100644 for (i = 0; i < VC_MAX_CHANNEL; i++) { sprintf(csi_name, "csi2_vc%d", i); -@@ -573,6 +673,8 @@ static int rcar_csi2_parse_dt(struct device_node *np, - config->vcdt |= (0x24 << (i * 16)); - else if (!strcmp(str, "ycbcr422")) - config->vcdt |= (0x1e << (i * 16)); -+ else if (!strcmp(str, "raw8")) -+ config->vcdt |= (0x2a << (i * 16)); - else - config->vcdt |= 0; + vc_ch = of_get_child_by_name(vc_np, csi_name); + if (!vc_ch) + continue; +- ret = of_property_read_string(vc_ch, "data,type", &str); +- if (ret < 0) +- return ret; + ret = of_property_read_u32(vc_ch, "receive,vc", &ch); + if (ret < 0) + return ret; -@@ -587,6 +689,8 @@ static int rcar_csi2_parse_dt(struct device_node *np, - config->vcdt2 |= (0x24 << (j * 16)); - else if (!strcmp(str, "ycbcr422")) - config->vcdt2 |= (0x1e << (j * 16)); -+ else if (!strcmp(str, "raw8")) -+ config->vcdt2 |= (0x2a << (j * 16)); - else - config->vcdt2 |= 0; + if (i < 2) { +- if (!strcmp(str, "rgb888")) +- config->vcdt |= (0x24 << (i * 16)); +- else if (!strcmp(str, "ycbcr422")) +- config->vcdt |= (0x1e << (i * 16)); +- else +- config->vcdt |= 0; +- + config->vcdt |= (ch << (8 + (i * 16))); + config->vcdt |= (RCAR_CSI2_VCDT_VCDTN_EN << (i * 16)) | + (RCAR_CSI2_VCDT_SEL_DTN_ON << (i * 16)); +@@ -583,13 +673,6 @@ static int rcar_csi2_parse_dt(struct device_node *np, + if (i >= 2) { + int j = (i - 2); -@@ -608,6 +712,7 @@ static int rcar_csi2_probe(struct platform_device *pdev) +- if (!strcmp(str, "rgb888")) +- config->vcdt2 |= (0x24 << (j * 16)); +- else if (!strcmp(str, "ycbcr422")) +- config->vcdt2 |= (0x1e << (j * 16)); +- else +- config->vcdt2 |= 0; +- + config->vcdt2 |= (ch << (8 + (j * 16))); + config->vcdt2 |= (RCAR_CSI2_VCDT_VCDTN_EN << (j * 16)) | + (RCAR_CSI2_VCDT_SEL_DTN_ON << (j * 16)); +@@ -608,6 +691,7 @@ static int rcar_csi2_probe(struct platform_device *pdev) /* Platform data specify the PHY, lanes, ECC, CRC */ struct rcar_csi2_pdata *pdata; struct rcar_csi2_link_config link_config; @@ -10576,7 +17287,7 @@ index 4d95da6..2ef27e8 100644 dev_dbg(&pdev->dev, "CSI2 probed.\n"); -@@ -618,12 +723,7 @@ static int rcar_csi2_probe(struct platform_device *pdev) +@@ -618,12 +702,7 @@ static int rcar_csi2_probe(struct platform_device *pdev) if (ret) return ret; @@ -10590,7 +17301,7 @@ index 4d95da6..2ef27e8 100644 } else { pdata = pdev->dev.platform_data; if (!pdata) -@@ -655,23 +755,27 @@ static int rcar_csi2_probe(struct platform_device *pdev) +@@ -655,23 +734,27 @@ static int rcar_csi2_probe(struct platform_device *pdev) return ret; priv->pdev = pdev; @@ -10628,7 +17339,7 @@ index 4d95da6..2ef27e8 100644 spin_lock_init(&priv->lock); -@@ -684,10 +788,11 @@ static int rcar_csi2_probe(struct platform_device *pdev) +@@ -684,10 +767,11 @@ static int rcar_csi2_probe(struct platform_device *pdev) static int rcar_csi2_remove(struct platform_device *pdev) { @@ -11082,7 +17793,7 @@ index 74fb005..f6119e5 100644 goto cleanup; } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c -index edd1c1d..54f4c9d 100644 +index edd1c1d..ae04c6e 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -49,7 +49,7 @@ @@ -11094,7 +17805,7 @@ index edd1c1d..54f4c9d 100644 static DECLARE_BITMAP(device_map, MAP_MAX_NUM); static LIST_HEAD(hosts); static LIST_HEAD(devices); -@@ -1106,6 +1106,18 @@ static int soc_camera_s_parm(struct file *file, void *fh, +@@ -1106,6 +1106,44 @@ static int soc_camera_s_parm(struct file *file, void *fh, return -ENOIOCTLCMD; } @@ -11109,11 +17820,37 @@ index edd1c1d..54f4c9d 100644 + + return -ENOIOCTLCMD; +} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int soc_camera_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct soc_camera_device *icd = file->private_data; ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ ++ if (ici->ops->get_register) ++ return ici->ops->get_register(icd, reg); ++ ++ return -ENOIOCTLCMD; ++} ++ ++static int soc_camera_s_register(struct file *file, void *priv, ++ const struct v4l2_dbg_register *reg) ++{ ++ struct soc_camera_device *icd = file->private_data; ++ struct soc_camera_host *ici = to_soc_camera_host(icd->parent); ++ ++ if (ici->ops->set_register) ++ return ici->ops->set_register(icd, reg); ++ ++ return -ENOIOCTLCMD; ++} ++#endif + static int soc_camera_probe(struct soc_camera_host *ici, struct soc_camera_device *icd); -@@ -1664,7 +1676,7 @@ static void scan_of_host(struct soc_camera_host *ici) +@@ -1664,7 +1702,7 @@ static void scan_of_host(struct soc_camera_host *ici) of_node_put(ren); if (i) { @@ -11122,16 +17859,20 @@ index edd1c1d..54f4c9d 100644 break; } } -@@ -2077,6 +2089,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd) +@@ -2077,6 +2115,11 @@ static int soc_camera_device_register(struct soc_camera_device *icd) .vidioc_s_selection = soc_camera_s_selection, .vidioc_g_parm = soc_camera_g_parm, .vidioc_s_parm = soc_camera_s_parm, + .vidioc_g_edid = soc_camera_g_edid, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = soc_camera_g_register, ++ .vidioc_s_register = soc_camera_s_register, ++#endif }; static int video_dev_create(struct soc_camera_device *icd) diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c -index e3e665e..84754a4 100644 +index e3e665e..b47d515 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -57,6 +57,16 @@ @@ -11151,7 +17892,24 @@ index e3e665e..84754a4 100644 .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, .fmt = { .fourcc = V4L2_PIX_FMT_RGB555, -@@ -403,6 +413,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, +@@ -382,6 +392,16 @@ + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, ++}, { ++ .code = MEDIA_BUS_FMT_SBGGR16_1X16, ++ .fmt = { ++ .fourcc = V4L2_PIX_FMT_SBGGR16, ++ .name = "Bayer 16 RGGB", ++ .bits_per_sample = 16, ++ .packing = SOC_MBUS_PACKING_NONE, ++ .order = SOC_MBUS_ORDER_LE, ++ .layout = SOC_MBUS_LAYOUT_PACKED, ++ }, + }, + }; + +@@ -403,6 +423,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, *numerator = 2; *denominator = 1; return 0; @@ -11162,7 +17920,7 @@ index e3e665e..84754a4 100644 case SOC_MBUS_PACKING_1_5X8: *numerator = 3; *denominator = 2; -@@ -428,6 +442,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) +@@ -428,6 +452,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) case SOC_MBUS_PACKING_2X8_PADLO: case SOC_MBUS_PACKING_EXTEND16: return width * 2; @@ -11193,14 +17951,18 @@ index 2ff7737..e5f3f53 100644 SOC_MBUS_PACKING_VARIABLE, SOC_MBUS_PACKING_1_5X8, diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h -index 1a15c3e..dad1ed8 100644 +index 1a15c3e..76e90be 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h -@@ -125,6 +125,7 @@ struct soc_camera_host_ops { +@@ -125,6 +125,11 @@ struct soc_camera_host_ops { int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); unsigned int (*poll)(struct file *, poll_table *); + int (*get_edid)(struct soc_camera_device *, struct v4l2_edid *); ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ int (*get_register)(struct soc_camera_device *, struct v4l2_dbg_register *reg); ++ int (*set_register)(struct soc_camera_device *, const struct v4l2_dbg_register *reg); ++#endif }; #define SOCAM_SENSOR_INVERT_PCLK (1 << 0) -- cgit 1.2.3-korg From cda041377d8f94b08db2a96215a5689c40b9467b Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Fri, 13 Apr 2018 17:09:51 +0300 Subject: linux-renesas: rcar_vin: Add RAW sensors MBUS formats --- .../0130-Add-RAW-sensors-MBUS-formats.patch | 36 ++++++++++++++++++++++ .../linux/linux-renesas_4.9.bbappend | 1 + 2 files changed, 37 insertions(+) create mode 100644 meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0130-Add-RAW-sensors-MBUS-formats.patch diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0130-Add-RAW-sensors-MBUS-formats.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0130-Add-RAW-sensors-MBUS-formats.patch new file mode 100644 index 0000000..3278fc2 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0130-Add-RAW-sensors-MBUS-formats.patch @@ -0,0 +1,36 @@ +From f977b84f5899093208f64b31731ea991579d6f8f Mon Sep 17 00:00:00 2001 +From: Vladimir Barinov +Date: Fri, 13 Apr 2018 16:51:15 +0300 +Subject: [PATCH] Add RAW sensors MBUS formats. + +--- + drivers/media/platform/soc_camera/rcar_vin.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c +index 37f1a11..b4ea2de 100644 +--- a/drivers/media/platform/soc_camera/rcar_vin.c ++++ b/drivers/media/platform/soc_camera/rcar_vin.c +@@ -1074,6 +1074,9 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR12_1X12: ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ case MEDIA_BUS_FMT_SGRBG14_1X14: + vnmc |= VNMC_INF_RAW8 | VNMC_BPS; + break; + default: +@@ -2213,6 +2216,9 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR12_1X12: ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ case MEDIA_BUS_FMT_SGRBG14_1X14: + if (cam->extra_fmt) + break; + +-- +2.7.4 + diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend index 6c8337b..5f7f64a 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend @@ -109,6 +109,7 @@ SRC_URI_append = " \ file://0127-IMR-UIO-Driver-initial-version.patch \ file://0128-rcar_imr-v4l2-driver-Fix-module-support.patch \ file://0129-Add-cropping-handling-to-VSP-alpha-planes.patch \ + file://0130-Add-RAW-sensors-MBUS-formats.patch \ " SRC_URI_append_h3ulcb = " file://ulcb.cfg" -- cgit 1.2.3-korg From 4ae3ea734b05c22620e7108885f4d28305ceaae7 Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Fri, 13 Apr 2018 20:05:26 +0300 Subject: Fix ISP devicetree nodes to be compatiable with UIO driver --- .../0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch | 2 +- .../0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch index 0b67bc1..42b8f23 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0018-arm64-renesas-r8a7797-Add-Renesas-R8A7797-SoC-suppor.patch @@ -1239,7 +1239,7 @@ index 0000000..faefe8a + isp0: isp@fec00000 { + compatible = "renesas,isp-r8a7797"; + reg = <0 0xfec00000 0 0x20000>, -+ <0 0xfed00000 0 0x4000>; ++ <0 0xfed00000 0 0x10000>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 817>; diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch index 321258f..d9eb2fa 100644 --- a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0051-arm64-renesas-r8a7798-Add-Renesas-R8A7798-SoC-suppor.patch @@ -1784,7 +1784,7 @@ index 0000000..00bd4d6 + isp0: isp@fec00000 { + compatible = "renesas,isp-r8a7798"; + reg = <0 0xfec00000 0 0x20000>, -+ <0 0xfed00000 0 0x4000>; ++ <0 0xfed00000 0 0x10000>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 817>; @@ -1794,7 +1794,7 @@ index 0000000..00bd4d6 + isp1: isp@fee00000 { + compatible = "renesas,isp-r8a7798"; + reg = <0 0xfee00000 0 0x20000>, -+ <0 0xfed20000 0 0x4000>; ++ <0 0xfed20000 0 0x10000>; + interrupts = , + ; + clocks = <&cpg CPG_MOD 814>; -- cgit 1.2.3-korg From 1e40e21e5dc5da75931ed835c624147d25af4b55 Mon Sep 17 00:00:00 2001 From: Andrey Dolnikov Date: Mon, 16 Apr 2018 12:07:32 +0300 Subject: Add ISP UIO driver. --- meta-rcar-gen3-adas/conf/layer.conf | 9 + .../recipes-core/udev/udev-rules-cvlib.bb | 2 + .../udev/udev-rules-cvlib/54-isp.rules | 1 + .../kernel-module-uio-isp/files/GPL-COPYING | 339 ++++++++++++++++ .../kernel-module-uio-isp/files/MIT-COPYING | 41 ++ .../kernel-module-uio-isp/files/Makefile | 14 + .../kernel-module-uio-isp/files/uio_isp.c | 450 +++++++++++++++++++++ .../kernel-module-uio-isp_0.1.bb | 27 ++ 8 files changed, 883 insertions(+) create mode 100644 meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib/54-isp.rules create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/GPL-COPYING create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/MIT-COPYING create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/Makefile create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/uio_isp.c create mode 100644 meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/kernel-module-uio-isp_0.1.bb diff --git a/meta-rcar-gen3-adas/conf/layer.conf b/meta-rcar-gen3-adas/conf/layer.conf index 8524ff6..70bd486 100644 --- a/meta-rcar-gen3-adas/conf/layer.conf +++ b/meta-rcar-gen3-adas/conf/layer.conf @@ -96,6 +96,15 @@ IMAGE_INSTALL_append_r8a7798 += " \ udev-rules-cvlib \ " +# ISP +IMAGE_INSTALL_append_r8a7797 += " \ + kernel-module-uio-isp \ +" + +IMAGE_INSTALL_append_r8a7798 += " \ + kernel-module-uio-isp \ +" + DISTRO_FEATURES_remove="x11" DISTRO_FEATURES_append = " surroundview " DISTRO_FEATURES_append = " opencv-sdk " diff --git a/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib.bb b/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib.bb index 3cff673..023f6fd 100644 --- a/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib.bb +++ b/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib.bb @@ -5,6 +5,7 @@ SRC_URI = " \ file://51-imp.rules \ file://52-cmem.rules \ file://53-vip.rules \ + file://54-isp.rules \ " do_install () { @@ -12,4 +13,5 @@ do_install () { install -m 0644 ${WORKDIR}/51-imp.rules ${D}${sysconfdir}/udev/rules.d/ install -m 0644 ${WORKDIR}/52-cmem.rules ${D}${sysconfdir}/udev/rules.d/ install -m 0644 ${WORKDIR}/53-vip.rules ${D}${sysconfdir}/udev/rules.d/ + install -m 0644 ${WORKDIR}/54-isp.rules ${D}${sysconfdir}/udev/rules.d/ } diff --git a/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib/54-isp.rules b/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib/54-isp.rules new file mode 100644 index 0000000..e4e1b28 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-core/udev/udev-rules-cvlib/54-isp.rules @@ -0,0 +1 @@ +SUBSYSTEM=="uio", ATTR{name}=="*isp*", SYMLINK+="$attr{name}", MODE="0666" diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/GPL-COPYING b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/GPL-COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/GPL-COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/MIT-COPYING b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/MIT-COPYING new file mode 100644 index 0000000..28e2a15 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/MIT-COPYING @@ -0,0 +1,41 @@ + +This software is Copyright (C) 2013 Renesas Electronics Corporation + +You may use, distribute and copy this software under the terms of the MIT +license displayed below. + +----------------------------------------------------------------------------- + + 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, sublicense, 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 shall be included in + all copies or substantial portions of the Software. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 2 ("GPL") in which case the provisions + of GPL are applicable instead of those above. + + If you wish to allow use of your version of this file only under the terms of + GPL, and not to allow others to use your version of this file under the terms + of the MIT license, indicate your decision by deleting the provisions above + and replace them with the notice and other provisions required by GPL as set + out in the file called "GPL-COPYING" included in this distribution. If you do + not delete the provisions above, a recipient may use your version of this file + under the terms of either the MIT license or GPL. + +----------------------------------------------------------------------------- + + EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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 NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS 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. + +----------------------------------------------------------------------------- diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/Makefile b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/Makefile new file mode 100644 index 0000000..33c2ced --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/Makefile @@ -0,0 +1,14 @@ +obj-m := uio_isp.o + +SRC := $(shell pwd) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) + +modules_install: + $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install + +clean: + rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c + rm -f Module.markers Module.symvers modules.order + rm -rf .tmp_versions Modules.symvers diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/uio_isp.c b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/uio_isp.c new file mode 100644 index 0000000..5898b01 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/files/uio_isp.c @@ -0,0 +1,450 @@ +/** + * This driver module supports both the ISP itself and the ISP Wrapper + * by providing the user with two separate UIO devices in /dev, one for the + * ISP and one for the ISP Wrapper. The struct uio_isp_platform_data contains + * the uio_info for the ISP as well as the ISP Wrapper. + * The ISP has to be defined in the Device Tree using the following structure + isp0: isp@fec00000 { + compatible = "renesas,isp-r8a7797"; + reg = <0 0xfec00000 0 0x20000>, <0 0xfed00000 0 0x10000>; + interrupts = , ; + clocks = <&cpg CPG_MOD 817>; + power-domains = <&sysc R8A7797_PD_ALWAYS_ON>; + }; + * The first reg entry is the base and size of the ISP memory area. + * The second reg entry is the base and size of the ISP Wrapper memory area. + * The first interrupt entry is for the ISP. + * The second interrupt entry is for the ISP Wrapper + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* ISP internals */ +#define ISP_INTERNAL_REG_SIZE 0X20000 +#define ISP_INT_FRAME_START_MASK 0x84 +#define ISP_INT_FRAME_START_STATUS 0x88 +#define ISP_INT_FRAME_START_CLEAR 0x8c +#define ISP_INT_FRAME_END_MASK 0x98 +#define ISP_INT_FRAME_END_STATUS 0x9c +#define ISP_INT_FRAME_END_CLEAR 0xa0 +#define ISP_INT_STATS_MASK 0xb0 +#define ISP_INT_STATS_STATUS 0xb4 +#define ISP_INT_STATS_CLEAR 0xb8 + +/* ISP Wrapper internals */ +#define ISPVCR 0x00 +#define ISPINT_STATUS 0x40 +#define ISPINT_ENABLE 0x60 +#define ISPERR0_ENABLE 0x64 + +/* ISP uio driver name */ +#define DRIVER_NAME "uio_isp" +#define DRIVER_VERSION "0.1" + +/* Platform data for ISP and ISP Wrapper */ +struct uio_isp_platform_data +{ + struct platform_device *pdev; + struct uio_info *uio_info[2]; /* [0]: ISP [1]: ISP Wrapper */ + struct clk *isp_clock; + spinlock_t isp_lock; + spinlock_t wrapper_lock; + unsigned long isp_flags; + unsigned long wrapper_flags; + void __iomem *base_addr; + void __iomem *wrapper_addr; +}; + +/* ISP Wrapper function prototypes */ +static int isp_wrapper_probe(struct platform_device *pdev, struct uio_isp_platform_data * const priv); +static int isp_wrapper_open(struct uio_info *uio_info, struct inode *inode); +static int isp_wrapper_close(struct uio_info *uio_info, struct inode *inode); +static irqreturn_t isp_wrapper_irq_handler(int irq, struct uio_info *uio_info); + +/* ISP function prototypes */ +static int isp_probe(struct platform_device *pdev, struct uio_isp_platform_data * const priv); +static int isp_open(struct uio_info *uio_info, struct inode *inode); +static int isp_close(struct uio_info *uio_info, struct inode *inode); +static irqreturn_t isp_irq_handler(int irq, struct uio_info *uio_info); + +/* Common functions for ISP and ISP Wrapper */ +static int irq_control(struct uio_info *uio_info, s32 irq_on); + +static inline u32 read_reg(void * const base, const u32 offset) +{ + return ioread32((u8 *)base + offset); +} + +static inline void write_reg(void * const base, const u32 offset, const u32 val) +{ + iowrite32(val, (u8 *)base + offset); +} + +/* Function definitions */ +static int isp_wrapper_probe(struct platform_device *pdev, struct uio_isp_platform_data * const priv) +{ + struct uio_info *uio_info; + struct uio_mem *uio_mem; + struct resource *res; + char *wrapper_name; + int err; + struct device *dev = &pdev->dev; + + /* Construct the uio_info structure */ + uio_info = devm_kzalloc(dev, sizeof(*uio_info), GFP_KERNEL); + wrapper_name = devm_kmalloc(dev, strlen(pdev->name) + strlen("_wrapper") + 1, GFP_KERNEL); + if (!uio_info || !wrapper_name) + return -ENOMEM; + sprintf(wrapper_name, "%s_wrapper", pdev->name); + uio_info->version = DRIVER_VERSION; + uio_info->open = isp_wrapper_open; + uio_info->release = isp_wrapper_close; + uio_info->name = wrapper_name; + + /* Set up memory resource */ + uio_mem = &uio_info->mem[0]; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "failed to get ISP Wrapper memory\n"); + return -EINVAL; + } + uio_mem->memtype = UIO_MEM_PHYS; + uio_mem->addr = res->start; + uio_mem->size = resource_size(res); + uio_mem->name = res->name; + if (!devm_request_mem_region(dev, res->start, resource_size(res), uio_info->name)) { + dev_err(dev, "failed to reserve ISP Wrapper memory\n"); + return -ENOMEM; + } + priv->wrapper_addr = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!priv->wrapper_addr) { + dev_err(dev, "failed to map ISP Wrapper memory\n"); + return -EIO; + } + + /* Set up interrupt */ + write_reg(priv->wrapper_addr, ISPINT_ENABLE, 0); + write_reg(priv->wrapper_addr, ISPERR0_ENABLE, 0); + uio_info->irq = irq_of_parse_and_map(pdev->dev.of_node, 1); + if (uio_info->irq == -ENXIO) { + dev_err(dev, "failed to parse ISP Wrapper interrupt\n"); + return -EINVAL; + } + uio_info->handler = isp_wrapper_irq_handler; + uio_info->irqcontrol = irq_control; + + /* Register the UIO device */ + if ((err = uio_register_device(dev, uio_info))) { + dev_err(dev, "failed to register UIO device for ISP Wrapper\n"); + return err; + } + + /* Register this uio_info with the platform data */ + uio_info->priv = priv; + priv->uio_info[1] = uio_info; + spin_lock_init(&priv->wrapper_lock); + priv->wrapper_flags = 1; /* The ISP Wrapper interrupt is active */ + + /* Print some information */ + dev_dbg(dev, "ISP Wrapper irq %ld\n", uio_info->irq); + + return 0; +} + +static int isp_wrapper_open(struct uio_info *uio_info, struct inode *inode) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + + dev_dbg(&priv->pdev->dev, "isp wrapper open\n"); + + return 0; +} + +static int isp_wrapper_close(struct uio_info *uio_info, struct inode *inode) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + + dev_dbg(&priv->pdev->dev, "isp wrapper close\n"); + + return 0; +} + +static irqreturn_t isp_wrapper_irq_handler(int irq, struct uio_info *uio_info) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + u32 ispint_status = read_reg(priv->wrapper_addr, ISPINT_STATUS); + + /* Mask all interrupts */ + write_reg(priv->wrapper_addr, ISPINT_ENABLE, 0); + + if (!ispint_status) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int isp_probe(struct platform_device *pdev, struct uio_isp_platform_data * const priv) +{ + struct uio_info *uio_info; + struct uio_mem *uio_mem; + struct resource *res; + int err; + struct device *dev = &pdev->dev; + + /* Construct the uio_info structure */ + uio_info = devm_kzalloc(dev, sizeof(*uio_info), GFP_KERNEL); + if (!uio_info) + return -ENOMEM; + uio_info->version = DRIVER_VERSION; + uio_info->open = isp_open; + uio_info->release = isp_close; + uio_info->name = pdev->name; + + /* Set up memory resource */ + uio_mem = &uio_info->mem[0]; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get ISP memory\n"); + return -EINVAL; + } + uio_mem->memtype = UIO_MEM_PHYS; + uio_mem->addr = res->start; + uio_mem->size = resource_size(res); + uio_mem->name = res->name; + if (!devm_request_mem_region(dev, res->start, resource_size(res), uio_info->name)) { + dev_err(dev, "failed to reserve ISP memory\n"); + return -ENOMEM; + } + priv->base_addr = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!priv->base_addr) { + dev_err(dev, "failed to map ISP memory\n"); + return -EIO; + } + + /* Set up interrupt */ + write_reg(priv->base_addr, ISP_INT_FRAME_START_MASK, 0); + write_reg(priv->base_addr, ISP_INT_FRAME_END_MASK, 0); + write_reg(priv->base_addr, ISP_INT_STATS_MASK, 0); + uio_info->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (uio_info->irq == -ENXIO) { + dev_err(dev, "failed to parse ISP interrupt\n"); + return -EINVAL; + } + uio_info->handler = isp_irq_handler; + uio_info->irqcontrol = irq_control; + + /* Set up clock */ + priv->isp_clock = devm_clk_get(dev, NULL); + if (IS_ERR(priv->isp_clock)) { + dev_err(dev, "failed to get ISP clock info\n"); + return -ENODEV; + } + + /* Register the UIO device */ + if ((err = uio_register_device(dev, uio_info))) { + dev_err(dev, "failed to register UIO device for ISP\n"); + return err; + } + + /* Register the uio_info with the platform data */ + uio_info->priv = priv; + priv->uio_info[0] = uio_info; + spin_lock_init(&priv->isp_lock); + priv->isp_flags = 1; /* ISP interrupt is active */ + + /* Register the UIO device with the PM framework */ + pm_runtime_enable(dev); + + /* Enable clocks */ + if ((err = clk_prepare_enable(priv->isp_clock))) { + dev_err(dev, "failed to enable clock\n"); + return -EBUSY; + } + + /* Print some information */ + dev_dbg(dev, "ISP irq %ld clock %ld Hz\n", uio_info->irq, clk_get_rate(priv->isp_clock)); + + return 0; +} + +static int isp_open(struct uio_info *uio_info, __attribute__((unused)) struct inode *inode) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + + dev_dbg(&priv->pdev->dev, "isp open\n"); + /* Wait until the PM has woken up the device */ + pm_runtime_get_sync(&priv->pdev->dev); + + if (!test_and_set_bit(0, &priv->isp_flags)) + enable_irq((unsigned int)uio_info->irq); + + return 0; +} + +static int isp_close(struct uio_info *uio_info, __attribute__((unused)) struct inode *inode) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + + dev_dbg(&priv->pdev->dev, "isp close\n"); + /* Tell the PM that the device has become idle */ + pm_runtime_put_sync(&priv->pdev->dev); + + if (test_and_clear_bit(0, &priv->isp_flags)) + disable_irq((unsigned int)uio_info->irq); + + return 0; +} + +static irqreturn_t isp_irq_handler(int irq, struct uio_info *uio_info) +{ + struct uio_isp_platform_data *priv = uio_info->priv; + u32 fstart_status; + u32 fend_status; + u32 stats_status; + + /* Mask all interrupts, umask by user process */ + write_reg(priv->base_addr, ISP_INT_FRAME_START_MASK, 0); + write_reg(priv->base_addr, ISP_INT_FRAME_END_MASK, 0); + write_reg(priv->base_addr, ISP_INT_STATS_MASK, 0); + + fstart_status = read_reg(priv->base_addr, ISP_INT_FRAME_START_STATUS); + fend_status = read_reg(priv->base_addr, ISP_INT_FRAME_END_STATUS); + stats_status = read_reg(priv->base_addr, ISP_INT_STATS_STATUS); + + if (!(fstart_status | fend_status | stats_status)) { + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int irq_control(struct uio_info *uio_info, s32 irq_on) +{ + struct uio_isp_platform_data *const priv = uio_info->priv; + unsigned long flag; + + if (uio_info == priv->uio_info[0]) { + dev_dbg(&priv->pdev->dev, "ISP irq_control\n"); + /* This is the ISP */ + spin_lock_irqsave(&priv->isp_lock, flag); + if (irq_on) { + if (!test_and_set_bit(0, &priv->isp_flags)) { + enable_irq((unsigned int)uio_info->irq); + } + } + else { + if (test_and_clear_bit(0, &priv->isp_flags)) { + disable_irq((unsigned int)uio_info->irq); + } + } + spin_unlock_irqrestore(&priv->isp_lock, flag); + } else { + dev_dbg(&priv->pdev->dev, "ISP Wrapper irq_control\n"); + /* This is the ISP Wrapper */ + spin_lock_irqsave(&priv->wrapper_lock, flag); + if (irq_on) { + if (!test_and_set_bit(0, &priv->wrapper_flags)) { + enable_irq((unsigned int)uio_info->irq); + } + } + else { + if (test_and_clear_bit(0, &priv->wrapper_flags)) { + disable_irq((unsigned int)uio_info->irq); + } + } + spin_unlock_irqrestore(&priv->wrapper_lock, flag); + } + + return 0; +} + +static int uio_isp_probe(struct platform_device *pdev) +{ + struct uio_isp_platform_data *priv; + int err; + + /* Allocate memory for the common housekeeping structure */ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->pdev = pdev; + + if ((err = isp_wrapper_probe(pdev, priv))) + return err; + if ((err = isp_probe(pdev, priv))) + return err; + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int uio_isp_remove(struct platform_device *pdev) +{ + struct uio_isp_platform_data *priv = platform_get_drvdata(pdev); + + uio_unregister_device(priv->uio_info[0]); + uio_unregister_device(priv->uio_info[1]); + + clk_disable_unprepare(priv->isp_clock); + pm_runtime_disable(&priv->pdev->dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* Power management */ +static int uio_runtime_isp_nop(__attribute__((unused)) struct device *dev) +{ + return 0; +}; + +static const struct dev_pm_ops uio_dev_pm_isp_ops = +{ + .runtime_suspend = &uio_runtime_isp_nop, + .runtime_resume = &uio_runtime_isp_nop, +}; + +/* Compatible table */ +static const struct of_device_id rcar_isp_dt_ids[] = +{ + { .compatible = "renesas,isp-r8a7797", .data = 0 }, + { .compatible = "renesas,isp-r8a7798", .data = 0 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_isp_dt_ids); + +/* Platform driver structure */ +static struct platform_driver uio_isp_platform_driver = +{ + .probe = &uio_isp_probe, + .remove = &uio_isp_remove, + .driver = + { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &uio_dev_pm_isp_ops, + .of_match_table = of_match_ptr(rcar_isp_dt_ids) + } +}; +module_platform_driver(uio_isp_platform_driver); + +MODULE_AUTHOR("Renesas Electronics Corporation"); +MODULE_DESCRIPTION("Userspace I/O driver for ISP"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/kernel-module-uio-isp_0.1.bb b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/kernel-module-uio-isp_0.1.bb new file mode 100644 index 0000000..ed3a742 --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/kernel-module-uio-isp/kernel-module-uio-isp_0.1.bb @@ -0,0 +1,27 @@ +SUMMARY = "ISP UIO Driver Module" +LICENSE = "GPLv2 & MIT" +LIC_FILES_CHKSUM = " \ + file://GPL-COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \ + file://MIT-COPYING;md5=fea016ce2bdf2ec10080f69e9381d378 \ + " + +inherit module + +PR = "r0" +PV = "0.1" + +SRC_URI = " \ + file://Makefile \ + file://uio_isp.c \ + file://GPL-COPYING \ + file://MIT-COPYING \ + " + +S = "${WORKDIR}" + +KERNEL_MODULE_AUTOLOAD += "uio_isp" +KERNEL_MODULE_PROBECONF += "uio_isp" + +module_conf_uio_isp = "" +module_conf_uio_isp_r8a7795 = "options uio_isp clear_int" +module_conf_uio_isp_r8a7796 = "options uio_isp clear_int" -- cgit 1.2.3-korg